From 71f13f0ab8c44ab3932fae882b0e6100e5676053 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 9 May 2023 16:43:33 +0100 Subject: [PATCH 001/250] Create IS-12 websocket server --- Development/cmake/NmosCppLibraries.cmake | 3 + Development/nmos/control_protocol_ws_api.cpp | 84 ++++++++++++++++++++ Development/nmos/control_protocol_ws_api.h | 33 ++++++++ Development/nmos/is12_versions.h | 26 ++++++ Development/nmos/node_resources.cpp | 21 +++++ Development/nmos/node_server.cpp | 15 +++- Development/nmos/settings.cpp | 3 + Development/nmos/settings.h | 4 + 8 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 Development/nmos/control_protocol_ws_api.cpp create mode 100644 Development/nmos/control_protocol_ws_api.h create mode 100644 Development/nmos/is12_versions.h diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index b6d6d972a..e3e365cf8 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -757,6 +757,7 @@ set(NMOS_CPP_NMOS_SOURCES nmos/connection_api.cpp nmos/connection_events_activation.cpp nmos/connection_resources.cpp + nmos/control_protocol_ws_api.cpp nmos/did_sdid.cpp nmos/events_api.cpp nmos/events_resources.cpp @@ -828,6 +829,7 @@ set(NMOS_CPP_NMOS_HEADERS nmos/connection_api.h nmos/connection_events_activation.h nmos/connection_resources.h + nmos/control_protocol_ws_api.h nmos/device_type.h nmos/did_sdid.h nmos/event_type.h @@ -846,6 +848,7 @@ set(NMOS_CPP_NMOS_HEADERS nmos/is07_versions.h nmos/is08_versions.h nmos/is09_versions.h + nmos/is12_versions.h nmos/json_fields.h nmos/json_schema.h nmos/lldp_handler.h diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp new file mode 100644 index 000000000..0a067d524 --- /dev/null +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -0,0 +1,84 @@ +#include "nmos/control_protocol_ws_api.h" + +#include "nmos/slog.h" + +namespace nmos +{ + // IS-12 Control Protocol WebSocket API + + web::websockets::experimental::listener::validate_handler make_control_protocol_ws_validate_handler(nmos::node_model& model, slog::base_gate& gate_) + { + return [&model, &gate_](web::http::http_request req) + { + nmos::ws_api_gate gate(gate_, req.request_uri()); + + // RFC 6750 defines two methods of sending bearer access tokens which are applicable to WebSocket + // Clients SHOULD use the "Authorization Request Header Field" method. + // Clients MAY use a "URI Query Parameter". + // See https://tools.ietf.org/html/rfc6750#section-2 + + // For now just return true + const auto& ws_ncp_path = req.request_uri().path(); + slog::log(gate, SLOG_FLF) << "Validating websocket connection to: " << ws_ncp_path; + + return true; + }; + } + + web::websockets::experimental::listener::open_handler make_control_protocol_ws_open_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate_) + { + return [&model, &websockets, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id) + { + nmos::ws_api_gate gate(gate_, connection_uri); + + const auto& ws_ncp_path = connection_uri.path(); + slog::log(gate, SLOG_FLF) << "Opening websocket connection to: " << ws_ncp_path; + { + // create a websocket connection resource + + nmos::id id = nmos::make_id(); + websockets.insert({ id, connection_id }); + + slog::log(gate, SLOG_FLF) << "Creating websocket connection: " << id; + } + }; + } + + web::websockets::experimental::listener::close_handler make_control_protocol_ws_close_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate_) + { + return [&model, &websockets, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id, web::websockets::websocket_close_status close_status, const utility::string_t& close_reason) + { + nmos::ws_api_gate gate(gate_, connection_uri); + + const auto& ws_ncp_path = connection_uri.path(); + slog::log(gate, SLOG_FLF) << "Closing websocket connection to: " << ws_ncp_path << " [" << (int)close_status << ": " << close_reason << "]"; + + auto websocket = websockets.right.find(connection_id); + if (websockets.right.end() != websocket) + { + slog::log(gate, SLOG_FLF) << "Deleting websocket connection"; + + websockets.right.erase(websocket); + } + }; + } + + web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate_) + { + return [&model, &websockets, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id, const web::websockets::websocket_incoming_message& msg_) + { + nmos::ws_api_gate gate(gate_, connection_uri); + // theoretically blocking, but in fact not + auto msg = msg_.extract_string().get(); + + const auto& ws_ncp_path = connection_uri.path(); + slog::log(gate, SLOG_FLF) << "Received websocket message: " << msg << " on connection to: " << ws_ncp_path; + + auto websocket = websockets.right.find(connection_id); + if (websockets.right.end() != websocket) + { + // hmm, todo message handling.... + } + }; + } +} diff --git a/Development/nmos/control_protocol_ws_api.h b/Development/nmos/control_protocol_ws_api.h new file mode 100644 index 000000000..1469666f0 --- /dev/null +++ b/Development/nmos/control_protocol_ws_api.h @@ -0,0 +1,33 @@ +#ifndef NMOS_CONTROL_PROTOCOL_WS_API_H +#define NMOS_CONTROL_PROTOCOL_WS_API_H + +#include "nmos/websockets.h" + +namespace slog +{ + class base_gate; +} + +// Events API websocket implementation +// See https://specs.amwa.tv/is-07/releases/v1.0.1/docs/5.2._Transport_-_Websocket.html +namespace nmos +{ + struct node_model; + + web::websockets::experimental::listener::validate_handler make_control_protocol_ws_validate_handler(nmos::node_model& model, slog::base_gate& gate); + web::websockets::experimental::listener::open_handler make_control_protocol_ws_open_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate); + web::websockets::experimental::listener::close_handler make_control_protocol_ws_close_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate); + web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate); + + inline web::websockets::experimental::listener::websocket_listener_handlers make_control_protocol_ws_api(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate) + { + return{ + nmos::make_control_protocol_ws_validate_handler(model, gate), + nmos::make_control_protocol_ws_open_handler(model, websockets, gate), + nmos::make_control_protocol_ws_close_handler(model, websockets, gate), + nmos::make_control_protocol_ws_message_handler(model, websockets, gate) + }; + } +} + +#endif diff --git a/Development/nmos/is12_versions.h b/Development/nmos/is12_versions.h new file mode 100644 index 000000000..06dfc1d59 --- /dev/null +++ b/Development/nmos/is12_versions.h @@ -0,0 +1,26 @@ +#ifndef NMOS_IS12_VERSIONS_H +#define NMOS_IS12_VERSIONS_H + +#include +#include +#include "nmos/api_version.h" +#include "nmos/settings.h" + +namespace nmos +{ + namespace is12_versions + { + const api_version v1_0{ 1, 0 }; + + const std::set all{ nmos::is12_versions::v1_0 }; + + inline std::set from_settings(const nmos::settings& settings) + { + return settings.has_field(nmos::fields::is12_versions) + ? boost::copy_range>(nmos::fields::is12_versions(settings) | boost::adaptors::transformed([](const web::json::value& v) { return nmos::parse_api_version(v.as_string()); })) + : nmos::is12_versions::all; + } + } +} + +#endif diff --git a/Development/nmos/node_resources.cpp b/Development/nmos/node_resources.cpp index 7c75cf01a..8daa2975d 100644 --- a/Development/nmos/node_resources.cpp +++ b/Development/nmos/node_resources.cpp @@ -16,6 +16,7 @@ #include "nmos/is05_versions.h" #include "nmos/is07_versions.h" #include "nmos/is08_versions.h" +#include "nmos/is12_versions.h" #include "nmos/media_type.h" #include "nmos/resource.h" #include "nmos/sdp_utils.h" // for nmos::make_components @@ -125,6 +126,26 @@ namespace nmos } } + if (0 <= nmos::fields::control_protocol_ws_port(settings)) + { + for (const auto& version : nmos::is12_versions::from_settings(settings)) + { + auto ncp_uri = web::uri_builder() + .set_scheme(nmos::ws_scheme(settings)) + .set_port(nmos::fields::control_protocol_ws_port(settings)) + .set_path(U("/x-nmos/ncp/") + make_api_version(version)); + auto type = U("urn:x-nmos:control:ncp/") + make_api_version(version); + + for (const auto& host : hosts) + { + web::json::push_back(data[U("controls")], value_of({ + { U("href"), ncp_uri.set_host(host).to_uri().to_string() }, + { U("type"), type } + })); + } + } + } + return{ is04_versions::v1_3, types::device, std::move(data), false }; } diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index ecc75c461..1f38c1a73 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -3,6 +3,7 @@ #include "cpprest/ws_utils.h" #include "nmos/api_utils.h" #include "nmos/channelmapping_activation.h" +#include "nmos/control_protocol_ws_api.h" #include "nmos/events_api.h" #include "nmos/events_ws_api.h" #include "nmos/logging_api.h" @@ -63,9 +64,16 @@ namespace nmos // Configure the Channel Mapping API node_server.api_routers[{ {}, nmos::fields::channelmapping_port(node_model.settings) }].mount({}, nmos::make_channelmapping_api(node_model, node_implementation.validate_map, gate)); - auto& events_ws_api = node_server.ws_handlers[{ {}, nmos::fields::events_ws_port(node_model.settings) }]; + const auto& events_ws_port = nmos::fields::events_ws_port(node_model.settings); + auto& events_ws_api = node_server.ws_handlers[{ {}, events_ws_port }]; events_ws_api.first = nmos::make_events_ws_api(node_model, events_ws_api.second, gate); + // can't share a port between the events ws and the control protocol ws + const auto& control_protocol_ws_port = nmos::fields::control_protocol_ws_port(node_model.settings); + if (control_protocol_ws_port == events_ws_port) throw std::runtime_error("Same port used for events and control protocol websockets are not supported"); + auto& control_protocol_ws_api = node_server.ws_handlers[{ {}, control_protocol_ws_port }]; + control_protocol_ws_api.first = nmos::make_control_protocol_ws_api(node_model, control_protocol_ws_api.second, gate); + // Set up the listeners for each HTTP API port auto http_config = nmos::make_http_listener_config(node_model.settings, node_implementation.load_server_certificates, node_implementation.load_dh_param, node_implementation.get_ocsp_response, gate); @@ -84,6 +92,7 @@ namespace nmos auto websocket_config = nmos::make_websocket_listener_config(node_model.settings, node_implementation.load_server_certificates, node_implementation.load_dh_param, node_implementation.get_ocsp_response, gate); websocket_config.set_log_callback(nmos::make_slog_logging_callback(gate)); + size_t event_ws_pos{ 0 }; for (auto& ws_handler : node_server.ws_handlers) { // if IP address isn't specified for this router, use default server address or wildcard address @@ -91,9 +100,11 @@ namespace nmos // map the configured client port to the server port on which to listen // hmm, this should probably also take account of the address node_server.ws_listeners.push_back(nmos::make_ws_api_listener(server_secure, host, nmos::experimental::server_port(ws_handler.first.second, node_model.settings), ws_handler.second.first, websocket_config, gate)); + + event_ws_pos = (ws_handler.first.second == events_ws_port) ? event_ws_pos : ++event_ws_pos; } - auto& events_ws_listener = node_server.ws_listeners.back(); + auto& events_ws_listener = node_server.ws_listeners.at(event_ws_pos); // Set up node operation (including the DNS-SD advertisements) diff --git a/Development/nmos/settings.cpp b/Development/nmos/settings.cpp index c8943c93e..7e6010d30 100644 --- a/Development/nmos/settings.cpp +++ b/Development/nmos/settings.cpp @@ -66,6 +66,8 @@ namespace nmos const auto http_port = nmos::fields::http_port(settings); // can't share a port between an http_listener and a websocket_listener, so use next higher port const auto ws_port = http_port + 1; + // can't share a port between the events ws and the control protocol ws + const auto ncp_ws_port = ws_port + 1; if (registry) web::json::insert(settings, std::make_pair(nmos::fields::query_port, http_port)); if (registry) web::json::insert(settings, std::make_pair(nmos::fields::query_ws_port, ws_port)); if (registry) web::json::insert(settings, std::make_pair(nmos::fields::registration_port, http_port)); @@ -81,6 +83,7 @@ namespace nmos if (registry) web::json::insert(settings, std::make_pair(nmos::experimental::fields::admin_port, http_port)); if (registry) web::json::insert(settings, std::make_pair(nmos::experimental::fields::mdns_port, http_port)); if (registry) web::json::insert(settings, std::make_pair(nmos::experimental::fields::schemas_port, http_port)); + if (!registry) web::json::insert(settings, std::make_pair(nmos::fields::control_protocol_ws_port, ncp_ws_port)); } } } diff --git a/Development/nmos/settings.h b/Development/nmos/settings.h index cf55267fd..1f240e21f 100644 --- a/Development/nmos/settings.h +++ b/Development/nmos/settings.h @@ -101,6 +101,9 @@ namespace nmos // is09_versions [registry, node]: used to specify the enabled API versions for a version-locked configuration const web::json::field_as_array is09_versions{ U("is09_versions") }; // when omitted, nmos::is09_versions::all is used + // is12_versions [node]: used to specify the enabled API versions for a version-locked configuration + const web::json::field_as_array is12_versions{ U("is12_versions") }; // when omitted, nmos::is12_versions::all is used + // pri [registry, node]: used for the 'pri' TXT record; specifying nmos::service_priorities::no_priority (maximum value) disables advertisement completely const web::json::field_as_integer_or pri{ U("pri"), 100 }; // default to highest_development_priority @@ -136,6 +139,7 @@ namespace nmos const web::json::field_as_integer_or channelmapping_port{ U("channelmapping_port"), 3215 }; // system_port [node]: used to construct request URLs for the System API (if not discovered via DNS-SD) const web::json::field_as_integer_or system_port{ U("system_port"), 10641 }; + const web::json::field_as_integer_or control_protocol_ws_port{ U("control_protocol_ws_port"), 3218 }; // listen_backlog [registry, node]: the maximum length of the queue of pending connections, or zero for the implementation default (the implementation may not honour this value) const web::json::field_as_integer_or listen_backlog{ U("listen_backlog"), 0 }; From c3d363793a20c191e6041c49b9bd08b14422811c Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 10 May 2023 01:36:00 +0100 Subject: [PATCH 002/250] Remove incorrect comment --- Development/nmos/control_protocol_ws_api.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Development/nmos/control_protocol_ws_api.h b/Development/nmos/control_protocol_ws_api.h index 1469666f0..71772961f 100644 --- a/Development/nmos/control_protocol_ws_api.h +++ b/Development/nmos/control_protocol_ws_api.h @@ -8,8 +8,6 @@ namespace slog class base_gate; } -// Events API websocket implementation -// See https://specs.amwa.tv/is-07/releases/v1.0.1/docs/5.2._Transport_-_Websocket.html namespace nmos { struct node_model; From 4728d7cbc0bae4f56893ba393d4d10ab5d9d0c75 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 10 May 2023 01:37:42 +0100 Subject: [PATCH 003/250] Add `control_protocol_ws_port` to node example config --- Development/nmos-cpp-node/config.json | 1 + 1 file changed, 1 insertion(+) diff --git a/Development/nmos-cpp-node/config.json b/Development/nmos-cpp-node/config.json index 5bf63a9ff..087db3683 100644 --- a/Development/nmos-cpp-node/config.json +++ b/Development/nmos-cpp-node/config.json @@ -134,6 +134,7 @@ //"channelmapping_port": 3215, // system_port [node]: used to construct request URLs for the System API (if not discovered via DNS-SD) //"system_port": 10641, + //"control_protocol_ws_port": 3218, // listen_backlog [registry, node]: the maximum length of the queue of pending connections, or zero for the implementation default (the implementation may not honour this value) //"listen_backlog": 0, From 549dd0ef5e0b0c94e6b23fe270741a72cdd98aa7 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Mon, 15 May 2023 19:09:11 +0100 Subject: [PATCH 004/250] Use lock to protect websockets --- Development/nmos/control_protocol_ws_api.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 0a067d524..89e11721a 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -1,5 +1,6 @@ #include "nmos/control_protocol_ws_api.h" +#include "nmos/model.h" #include "nmos/slog.h" namespace nmos @@ -30,6 +31,7 @@ namespace nmos return [&model, &websockets, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id) { nmos::ws_api_gate gate(gate_, connection_uri); + auto lock = model.write_lock(); const auto& ws_ncp_path = connection_uri.path(); slog::log(gate, SLOG_FLF) << "Opening websocket connection to: " << ws_ncp_path; @@ -49,6 +51,7 @@ namespace nmos return [&model, &websockets, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id, web::websockets::websocket_close_status close_status, const utility::string_t& close_reason) { nmos::ws_api_gate gate(gate_, connection_uri); + auto lock = model.write_lock(); const auto& ws_ncp_path = connection_uri.path(); slog::log(gate, SLOG_FLF) << "Closing websocket connection to: " << ws_ncp_path << " [" << (int)close_status << ": " << close_reason << "]"; @@ -68,7 +71,9 @@ namespace nmos return [&model, &websockets, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id, const web::websockets::websocket_incoming_message& msg_) { nmos::ws_api_gate gate(gate_, connection_uri); - // theoretically blocking, but in fact not + auto lock = model.read_lock(); + + // theoretically blocking, but in fact not auto msg = msg_.extract_string().get(); const auto& ws_ncp_path = connection_uri.path(); From 599a5f5635b22f5ee75376f6d4ac67282daaf97c Mon Sep 17 00:00:00 2001 From: lo-simon Date: Mon, 15 May 2023 19:11:52 +0100 Subject: [PATCH 005/250] Fix to obtain the event_ws position from the ws_handlers --- Development/nmos/node_server.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index 1f38c1a73..293784adc 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -93,6 +93,7 @@ namespace nmos websocket_config.set_log_callback(nmos::make_slog_logging_callback(gate)); size_t event_ws_pos{ 0 }; + bool found_event_ws{ false }; for (auto& ws_handler : node_server.ws_handlers) { // if IP address isn't specified for this router, use default server address or wildcard address @@ -101,7 +102,11 @@ namespace nmos // hmm, this should probably also take account of the address node_server.ws_listeners.push_back(nmos::make_ws_api_listener(server_secure, host, nmos::experimental::server_port(ws_handler.first.second, node_model.settings), ws_handler.second.first, websocket_config, gate)); - event_ws_pos = (ws_handler.first.second == events_ws_port) ? event_ws_pos : ++event_ws_pos; + if (!found_event_ws) + { + if (ws_handler.first.second == events_ws_port) { found_event_ws = true; } + else { ++event_ws_pos; } + } } auto& events_ws_listener = node_server.ws_listeners.at(event_ws_pos); From fae85fbfd500f4b8071708864be934fbe9c5a42d Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 3 Aug 2023 22:47:44 +0100 Subject: [PATCH 006/250] Create Root block, Device manager and Class manager nmos resources --- Development/cmake/NmosCppLibraries.cmake | 77 ++ Development/nmos-cpp-node/config.json | 14 + .../nmos-cpp-node/node_implementation.cpp | 12 + .../nmos/control_protocol_resources.cpp | 960 ++++++++++++++++++ Development/nmos/control_protocol_resources.h | 147 +++ Development/nmos/control_protocol_ws_api.cpp | 657 +++++++++++- Development/nmos/control_protocol_ws_api.h | 2 + Development/nmos/is12_schemas/is12_schemas.h | 25 + Development/nmos/json_fields.h | 88 ++ Development/nmos/json_schema.cpp | 56 + Development/nmos/json_schema.h | 4 + Development/nmos/model.h | 4 + Development/nmos/node_server.cpp | 12 +- Development/nmos/settings.h | 14 + Development/nmos/slog.h | 1 + Development/nmos/type.h | 7 + Development/third_party/is-12/README.md | 1 + .../v1.0.x/APIs/schemas/base-message.json | 23 + .../v1.0.x/APIs/schemas/command-message.json | 79 ++ .../schemas/command-response-message.json | 69 ++ .../v1.0.x/APIs/schemas/error-message.json | 38 + .../is-12/v1.0.x/APIs/schemas/event-data.json | 11 + .../APIs/schemas/notification-message.json | 72 ++ .../schemas/property-changed-event-data.json | 60 ++ .../APIs/schemas/subscription-message.json | 34 + .../subscription-response-message.json | 34 + 26 files changed, 2495 insertions(+), 6 deletions(-) create mode 100644 Development/nmos/control_protocol_resources.cpp create mode 100644 Development/nmos/control_protocol_resources.h create mode 100644 Development/nmos/is12_schemas/is12_schemas.h create mode 100644 Development/third_party/is-12/README.md create mode 100644 Development/third_party/is-12/v1.0.x/APIs/schemas/base-message.json create mode 100644 Development/third_party/is-12/v1.0.x/APIs/schemas/command-message.json create mode 100644 Development/third_party/is-12/v1.0.x/APIs/schemas/command-response-message.json create mode 100644 Development/third_party/is-12/v1.0.x/APIs/schemas/error-message.json create mode 100644 Development/third_party/is-12/v1.0.x/APIs/schemas/event-data.json create mode 100644 Development/third_party/is-12/v1.0.x/APIs/schemas/notification-message.json create mode 100644 Development/third_party/is-12/v1.0.x/APIs/schemas/property-changed-event-data.json create mode 100644 Development/third_party/is-12/v1.0.x/APIs/schemas/subscription-message.json create mode 100644 Development/third_party/is-12/v1.0.x/APIs/schemas/subscription-response-message.json diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index 001b7a17d..3265205c5 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -686,6 +686,80 @@ target_include_directories(nmos_is09_schemas PUBLIC list(APPEND NMOS_CPP_TARGETS nmos_is09_schemas) add_library(nmos-cpp::nmos_is09_schemas ALIAS nmos_is09_schemas) +# nmos_is12_schemas library + +set(NMOS_IS12_SCHEMAS_HEADERS + nmos/is12_schemas/is12_schemas.h + ) + +set(NMOS_IS12_V1_0_TAG v1.0.x) + +set(NMOS_IS12_V1_0_SCHEMAS_JSON + third_party/is-12/${NMOS_IS12_V1_0_TAG}/APIs/schemas/base-message.json + third_party/is-12/${NMOS_IS12_V1_0_TAG}/APIs/schemas/command-message.json + third_party/is-12/${NMOS_IS12_V1_0_TAG}/APIs/schemas/command-response-message.json + third_party/is-12/${NMOS_IS12_V1_0_TAG}/APIs/schemas/error-message.json + third_party/is-12/${NMOS_IS12_V1_0_TAG}/APIs/schemas/event-data.json + third_party/is-12/${NMOS_IS12_V1_0_TAG}/APIs/schemas/notification-message.json + third_party/is-12/${NMOS_IS12_V1_0_TAG}/APIs/schemas/property-changed-event-data.json + third_party/is-12/${NMOS_IS12_V1_0_TAG}/APIs/schemas/subscription-message.json + third_party/is-12/${NMOS_IS12_V1_0_TAG}/APIs/schemas/subscription-response-message.json + ) + +set(NMOS_IS12_SCHEMAS_JSON_MATCH "third_party/is-12/([^/]+)/APIs/schemas/([^;]+)\\.json") +set(NMOS_IS12_SCHEMAS_SOURCE_REPLACE "${CMAKE_CURRENT_BINARY_DIR_REPLACE}/nmos/is12_schemas/\\1/\\2.cpp") +string(REGEX REPLACE "${NMOS_IS12_SCHEMAS_JSON_MATCH}(;|$)" "${NMOS_IS12_SCHEMAS_SOURCE_REPLACE}\\3" NMOS_IS12_V1_0_SCHEMAS_SOURCES "${NMOS_IS12_V1_0_SCHEMAS_JSON}") + +foreach(JSON ${NMOS_IS12_V1_0_SCHEMAS_JSON}) + string(REGEX REPLACE "${NMOS_IS12_SCHEMAS_JSON_MATCH}" "${NMOS_IS12_SCHEMAS_SOURCE_REPLACE}" SOURCE "${JSON}") + string(REGEX REPLACE "${NMOS_IS12_SCHEMAS_JSON_MATCH}" "\\1" NS "${JSON}") + string(REGEX REPLACE "${NMOS_IS12_SCHEMAS_JSON_MATCH}" "\\2" VAR "${JSON}") + string(MAKE_C_IDENTIFIER "${NS}" NS) + string(MAKE_C_IDENTIFIER "${VAR}" VAR) + + file(WRITE "${SOURCE}.in" "\ +// Auto-generated from: ${JSON}\n\ +\n\ +namespace nmos\n\ +{\n\ + namespace is12_schemas\n\ + {\n\ + namespace ${NS}\n\ + {\n\ + const char* ${VAR} = R\"-auto-generated-(") + + file(READ "${JSON}" RAW) + file(APPEND "${SOURCE}.in" "${RAW}") + + file(APPEND "${SOURCE}.in" ")-auto-generated-\";\n\ + }\n\ + }\n\ +}\n") + + configure_file("${SOURCE}.in" "${SOURCE}" COPYONLY) +endforeach() + +add_library( + nmos_is12_schemas STATIC + ${NMOS_IS12_SCHEMAS_HEADERS} + ${NMOS_IS12_V1_0_SCHEMAS_SOURCES} + ) + +source_group("nmos\\is12_schemas\\Header Files" FILES ${NMOS_IS12_SCHEMAS_HEADERS}) +source_group("nmos\\is12_schemas\\${NMOS_IS12_V1_0_TAG}\\Source Files" FILES ${NMOS_IS12_V1_0_SCHEMAS_SOURCES}) + +target_link_libraries( + nmos_is12_schemas PRIVATE + nmos-cpp::compile-settings + ) +target_include_directories(nmos_is12_schemas PUBLIC + $ + $ + ) + +list(APPEND NMOS_CPP_TARGETS nmos_is12_schemas) +add_library(nmos-cpp::nmos_is12_schemas ALIAS nmos_is12_schemas) + # nmos-cpp library set(NMOS_CPP_BST_SOURCES @@ -757,6 +831,7 @@ set(NMOS_CPP_NMOS_SOURCES nmos/connection_api.cpp nmos/connection_events_activation.cpp nmos/connection_resources.cpp + nmos/control_protocol_resources.cpp nmos/control_protocol_ws_api.cpp nmos/did_sdid.cpp nmos/events_api.cpp @@ -830,6 +905,7 @@ set(NMOS_CPP_NMOS_HEADERS nmos/connection_api.h nmos/connection_events_activation.h nmos/connection_resources.h + nmos/control_protocol_resources.h nmos/control_protocol_ws_api.h nmos/device_type.h nmos/did_sdid.h @@ -988,6 +1064,7 @@ target_link_libraries( nmos-cpp::nmos_is05_schemas nmos-cpp::nmos_is08_schemas nmos-cpp::nmos_is09_schemas + nmos-cpp::nmos_is12_schemas nmos-cpp::mdns nmos-cpp::slog nmos-cpp::OpenSSL diff --git a/Development/nmos-cpp-node/config.json b/Development/nmos-cpp-node/config.json index 087db3683..bd42405ac 100644 --- a/Development/nmos-cpp-node/config.json +++ b/Development/nmos-cpp-node/config.json @@ -283,5 +283,19 @@ // ocsp_request_max [registry, node]: timeout for interactions with the OCSP server //"ocsp_request_max": 30, + // manufacturer_name [node]: the manufacturer name of the NcDeviceManager used for NMOS Control Protocol + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + //"manufacturer_name": "", + + // product_name/product_key/product_revision_level [node]: the product description of the NcDeviceManager used for NMOS Control Protocol + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncproduct + //"product_name": "", + //"product_key": "", + //"product_revision_level": "", + + // serial_number [node]: the serial number of the NcDeviceManager used for NMOS Control Protocol + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + //"serial_number": "", + "don't worry": "about trailing commas" } diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index dc3ee9d26..e72dcc24b 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -21,6 +21,7 @@ #include "nmos/colorspace.h" #include "nmos/connection_resources.h" #include "nmos/connection_events_activation.h" +#include "nmos/control_protocol_resources.h" #include "nmos/events_resources.h" #include "nmos/format.h" #include "nmos/group_hint.h" @@ -894,6 +895,17 @@ void node_implementation_init(nmos::node_model& model, slog::base_gate& gate) auto channelmapping_output = nmos::make_channelmapping_output(id, name, description, source_id, channel_labels, routable_inputs); if (!insert_resource_after(delay_millis, model.channelmapping_resources, std::move(channelmapping_output), gate)) throw node_implementation_init_exception(); } + + // example root block + auto root_block = nmos::make_root_block(); + // example device manager + auto device_manager = nmos::make_device_manager(2, root_block, model.settings); + if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(device_manager), gate)) throw node_implementation_init_exception(); + // example class manager + auto class_manager = nmos::make_class_manager(3, root_block); + if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(class_manager), gate)) throw node_implementation_init_exception(); + // insert root block to model + if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(root_block), gate)) throw node_implementation_init_exception(); } void node_implementation_run(nmos::node_model& model, slog::base_gate& gate) diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp new file mode 100644 index 000000000..1fa4316b8 --- /dev/null +++ b/Development/nmos/control_protocol_resources.cpp @@ -0,0 +1,960 @@ +#include "nmos/control_protocol_resources.h" + +#include "nmos/resource.h" +#include "nmos/is12_versions.h" + +namespace nmos +{ + namespace details + { + web::json::value make_control_protocol_result(const nc_method_result& method_result) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::status, method_result.status } + }); + } + + web::json::value make_control_protocol_error_result(const nc_method_result& method_result, const utility::string_t& error_message) + { + auto result = make_control_protocol_result(method_result); + if (!error_message.empty()) { result[nmos::fields::nc::error_message] = web::json::value::string(error_message); } + return result; + } + + web::json::value make_control_protocol_result(const nc_method_result& method_result, const web::json::value& value) + { + auto result = make_control_protocol_result(method_result); + result[nmos::fields::nc::value] = value; + return result; + } + + web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::handle, handle }, + { nmos::fields::nc::result, make_control_protocol_error_result(method_result, error_message) } + }, true); + } + + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::handle, handle }, + { nmos::fields::nc::result, make_control_protocol_result(method_result) } + }, true); + } + + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::handle, handle }, + { nmos::fields::nc::result, make_control_protocol_result(method_result, value) } + }, true); + } + + // message response + // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-message-type + web::json::value make_control_protocol_message_response(nc_message_type::type type, const web::json::value& responses) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::message_type, type }, + { nmos::fields::nc::responses, responses } + }, true); + }; + + // error message + // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#error-messages + web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::message_type, nc_message_type::error }, + { nmos::fields::nc::status, method_result.status}, + { nmos::fields::nc::error_message, error_message } + }, true); + }; + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid + web::json::value make_nc_class_id(const nc_class_id& class_id) + { + using web::json::value; + + auto nc_class_id = value::array(); + for (const auto class_id_item : class_id) { web::json::push_back(nc_class_id, class_id_item); } + return nc_class_id; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid + web::json::value make_nc_element_id(uint16_t level, uint16_t index) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::level, level }, + { nmos::fields::nc::index, index } + }); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventid + web::json::value make_nc_event_id(uint16_t level, uint16_t index) + { + return make_nc_element_id(level, index); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodid + web::json::value make_nc_method_id(uint16_t level, uint16_t index) + { + return make_nc_element_id(level, index); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertyid + web::json::value make_nc_property_id(uint16_t level, uint16_t index) + { + return make_nc_element_id(level, index); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanufacturer + web::json::value make_nc_manufacturer(const utility::string_t& name, const web::json::value& organization_id = web::json::value::null(), const web::json::value& website = web::json::value::null()) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::name, name }, + { nmos::fields::nc::organization_id, organization_id }, + { nmos::fields::nc::website, website } + }, true); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncproduct + // brand_name can be null + // uuid can be null + // description can be null + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const web::json::value& brand_name = web::json::value::null(), const web::json::value& uuid = web::json::value::null(), const web::json::value& description = web::json::value::null()) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::name, name }, + { nmos::fields::nc::key, key }, + { nmos::fields::nc::revision_level, revision_level }, + { nmos::fields::nc::brand_name, brand_name }, + { nmos::fields::nc::uuid, uuid }, + { nmos::fields::nc::description, description } + }, true); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdeviceoperationalstate + // device_specific_details can be null + web::json::value make_nc_device_operational_state(nc_device_generic_state::state generic_state, const web::json::value& device_specific_details) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::generic_state, generic_state }, + { nmos::fields::nc::device_specific_details, device_specific_details } + }, true); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdescriptor + // description can be null + web::json::value make_nc_descriptor(const web::json::value& description) + { + using web::json::value_of; + + return value_of({ { nmos::fields::nc::description, description } }); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblockmemberdescriptor + // description can be null + // user_label can be null + web::json::value make_nc_block_member_descriptor(const web::json::value& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const web::json::value& class_id, const web::json::value& user_label, nc_oid owner) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::role] = value::string(role); + data[nmos::fields::nc::oid] = oid; + data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); + data[nmos::fields::nc::class_id] = class_id; + data[nmos::fields::nc::user_label] = user_label; + data[nmos::fields::nc::owner] = owner; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassdescriptor + // description can be null + // fixedRole can be null + web::json::value make_nc_class_descriptor(const web::json::value& description, const web::json::value& class_id, const utility::string_t& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::class_id] = class_id; + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::fixed_role] = fixed_role; + data[nmos::fields::nc::properties] = properties; + data[nmos::fields::nc::methods] = methods; + data[nmos::fields::nc::events] = events; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncenumitemdescriptor + // description can be null + web::json::value make_nc_enum_item_descriptor(const web::json::value& description, const utility::string_t& name, uint16_t val) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::value] = val; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventdescriptor + // description can be null + // id = make_nc_event_id(level, index) + web::json::value make_nc_event_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const utility::string_t& event_datatype, bool is_deprecated) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::id] = id; + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::event_datatype] = value::string(event_datatype); + data[nmos::fields::nc::is_deprecated] = value::boolean(is_deprecated); + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncfielddescriptor + // description can be null + // type_name can be null + // constraints can be null + web::json::value make_nc_field_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::type_name] = type_name; + data[nmos::fields::nc::is_nullable] = value::boolean(is_nullable); + data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); + data[nmos::fields::nc::constraints] = constraints; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethoddescriptor + // description can be null + // id = make_nc_method_id(level, index) + // sequence parameters + web::json::value make_nc_method_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::id] = id; + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::result_datatype] = value::string(result_datatype); + data[nmos::fields::nc::parameters] = parameters; + data[nmos::fields::nc::is_deprecated] = value::boolean(is_deprecated); + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncparameterdescriptor + // description can be null + // type_name can be null + web::json::value make_nc_parameter_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::type_name] = type_name; + data[nmos::fields::nc::is_nullable] = value::boolean(is_nullable); + data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); + data[nmos::fields::nc::constraints] = constraints; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertydescriptor + // description can be null + // id = make_nc_property_id(level, index); + // type_name can be null + // constraints can be null + web::json::value make_nc_property_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const web::json::value& type_name, + bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::id] = id; + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::type_name] = type_name; + data[nmos::fields::nc::is_read_only] = value::boolean(is_read_only); + data[nmos::fields::nc::is_nullable] = value::boolean(is_nullable); + data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); + data[nmos::fields::nc::is_deprecated] = value::boolean(is_deprecated); + data[nmos::fields::nc::constraints] = constraints; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptor + // description can be null + // constraints can be null + web::json::value make_nc_datatype_descriptor(const web::json::value& description, const utility::string_t& name, nc_datatype_type::type type, const web::json::value& constraints) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::type] = type; + data[nmos::fields::nc::constraints] = constraints; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorenum + // description can be null + // constraints can be null + // items: sequence + web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const web::json::value& items) + { + auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Enum, constraints); + data[nmos::fields::nc::items] = items; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorprimitive + // description can be null + // constraints can be null + web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints) + { + return make_nc_datatype_descriptor(description, name, nc_datatype_type::Primitive, constraints); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorstruct + // description can be null + // constraints can be null + // fields: sequence + // parent_type can be null + web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const web::json::value& fields, const web::json::value& parent_type) + { + auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Struct, constraints); + data[nmos::fields::nc::fields] = fields; + data[nmos::fields::nc::parent_type] = parent_type; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptortypedef + // description can be null + // constraints can be null + web::json::value make_nc_datatype_typedef(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const utility::string_t& parent_type, bool is_sequence) + { + using web::json::value; + + auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Typedef, constraints); + data[nmos::fields::nc::parent_type] = value::string(parent_type); + data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject + web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + { + using web::json::value; + + const auto id = utility::conversions::details::to_string_t(oid); + auto data = make_resource_core(id, user_label.is_null() ? U("") : user_label.as_string(), {}); + data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); + data[nmos::fields::nc::oid] = oid; + data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); + data[nmos::fields::nc::owner] = owner; + data[nmos::fields::nc::role] = value::string(role); + data[nmos::fields::nc::user_label] = user_label; + data[nmos::fields::nc::touchpoints] = touchpoints; + data[nmos::fields::nc::runtime_property_constraints] = runtime_property_constraints; + + return data; + }; + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + web::json::value make_nc_block(nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members) + { + using web::json::value; + + auto data = details::make_nc_object({ 1, 1 }, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); + data[nmos::fields::nc::enabled] = value::boolean(enabled); + data[nmos::fields::nc::members] = members; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager + web::json::value make_nc_manager(nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null()) + { + return make_nc_object({ 1, 3 }, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, + const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, + const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause) + { + using web::json::value; + + auto data = details::make_nc_manager(oid, true, owner, U("DeviceManager"), user_label, touchpoints, runtime_property_constraints); + data[nmos::fields::nc::class_id] = details::make_nc_class_id({ 1, 3, 1 }); + data[nmos::fields::nc::nc_version] = value::string(U("v1.0")); + data[nmos::fields::nc::manufacturer] = manufacturer; + data[nmos::fields::nc::product] = product; + data[nmos::fields::nc::serial_number] = value::string(serial_number); + data[nmos::fields::nc::user_inventory_code] = user_inventory_code; + data[nmos::fields::nc::device_name] = device_name; + data[nmos::fields::nc::device_role] = device_role; + data[nmos::fields::nc::operational_state] = operational_state; + data[nmos::fields::nc::reset_cause] = reset_cause; + data[nmos::fields::nc::message] = value::null(); + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + web::json::value make_nc_class_manager(details::nc_oid oid, nc_oid owner, const web::json::value& user_label) + { + using web::json::value; + + auto data = details::make_nc_manager(oid, true, owner, U("ClassManager"), user_label); + data[nmos::fields::nc::class_id] = details::make_nc_class_id({ 1, 3, 2 }); + + // load the minimal control classes + data[nmos::fields::nc::control_classes] = value::array(); + auto& control_classes = data[nmos::fields::nc::control_classes]; + + // NcObject control class + { + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Static value. All instances of the same class will have the same identity value")), details::make_nc_property_id(1, 1), nmos::fields::nc::class_id, value::string(U("NcClassId")), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Object identifier")), details::make_nc_property_id(1, 2), nmos::fields::nc::oid, value::string(U("NcOid")), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("TRUE iff OID is hardwired into device")), details::make_nc_property_id(1, 3), nmos::fields::nc::constant_oid, value::string(U("NcBoolean")), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("OID of containing block. Can only ever be null for the root block")), details::make_nc_property_id(1, 4), nmos::fields::nc::owner, value::string(U("NcOid")), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Role of object in the containing block")), details::make_nc_property_id(1, 5), nmos::fields::nc::role, value::string(U("NcString")), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Scribble strip")), details::make_nc_property_id(1, 6), nmos::fields::nc::user_label, value::string(U("NcString")), false, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Touchpoints to other contexts")), details::make_nc_property_id(1, 7), nmos::fields::nc::touchpoints, value::string(U("NcTouchpoint")), true, true, true, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Runtime property constraints")), details::make_nc_property_id(1, 8), nmos::fields::nc::runtime_property_constraints, value::string(U("NcPropertyConstraints")), true, true, true, false, value::null())); + auto methods = value::array(); + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Get property value")), details::make_nc_method_id(1, 1), U("Get"), U("NcMethodResultPropertyValue"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Property value")), nmos::fields::nc::value, value::null(), true, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Set property value")), details::make_nc_method_id(1, 2), U("Set"), U("NcMethodResult"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Get sequence item")), details::make_nc_method_id(1, 3), U("GetSequenceItem"), U("NcMethodResultPropertyValue"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Value")), nmos::fields::nc::value, value::null(), true, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Set sequence item value")), details::make_nc_method_id(1, 4), U("SetSequenceItem"), U("NcMethodResult"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Value")), nmos::fields::nc::value, value::null(), true, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Add item to sequence")), details::make_nc_method_id(1, 5), U("AddSequenceItem"), U("NcMethodResultId"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Delete sequence item")), details::make_nc_method_id(1, 6), U("RemoveSequenceItem"), U("NcMethodResult"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Get sequence length")), details::make_nc_method_id(1, 7), U("GetSequenceLength"), U("NcMethodResultLength"), parameters, false)); + } + auto events = value::array(); + web::json::push_back(events, details::make_nc_event_descriptor(value::string(U("Property changed event")), details::make_nc_event_id(1, 1), U("PropertyChanged"), U("NcPropertyChangedEventData"), false)); + + web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcObject class descriptor")), details::make_nc_class_id({ 1 }), U("NcObject"), value::null(), properties, methods, events)); + } + + // NcBlock control class + { + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("TRUE if block is functional")), details::make_nc_property_id(2, 1), nmos::fields::nc::enabled, value::string(U("NcBoolean")), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Descriptors of this block's members")), details::make_nc_property_id(2, 2), nmos::fields::nc::members, value::string(U("NcBlockMemberDescriptor")), true, false, true, false, value::null())); + auto methods = value::array(); + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("If recurse is set to true, nested members can be retrieved")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Gets descriptors of members of the block")), details::make_nc_method_id(2, 1), U("GetMemberDescriptors"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Relative path to search for (MUST not include the role of the block targeted by oid)")), nmos::fields::nc::path, value::string(U("NcRolePath")), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Finds member(s) by path")), details::make_nc_method_id(2, 2), U("FindMembersByPath"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Role text to search for")), nmos::fields::nc::role, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Signals if the comparison should be case sensitive")), nmos::fields::nc::case_sensitive, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("TRUE to only return exact matches")), nmos::fields::nc::match_whole_string, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("TRUE to search nested blocks")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Finds members with given role name or fragment")), details::make_nc_method_id(2, 3), U("FindMembersByRole"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Class id to search for")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("If TRUE it will also include derived class descriptors")), nmos::fields::nc::include_derived, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("TRUE to search nested blocks")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Finds members with given class id")), details::make_nc_method_id(2, 4), U("FindMembersByClassId"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + } + auto events = value::array(); + + web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcBlock class descriptor")), details::make_nc_class_id({ 1, 1 }), U("NcBlock"), value::null(), properties, methods, events)); + } + + // NcWorker control class + { + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("TRUE iff worker is enabled")), details::make_nc_property_id(2, 1), nmos::fields::nc::enabled, value::string(U("NcBoolean")), false, false, false, false, value::null())); + auto methods = value::array(); + auto events = value::array(); + + web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcWorker class descriptor")), details::make_nc_class_id({ 1, 2 }), U("NcWorker"), value::null(), properties, methods, events)); + } + + // NcManager control class + { + auto properties = value::array(); + auto methods = value::array(); + auto events = value::array(); + + web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcManager class descriptor")), details::make_nc_class_id({ 1, 3 }), U("NcManager"), value::null(), properties, methods, events)); + } + + // NcDeviceManager control class + { + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Version of MS-05-02 that this device uses")), details::make_nc_property_id(3, 1), nmos::fields::nc::nc_version, value::string(U("NcVersionCode")), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Manufacturer descriptor")), details::make_nc_property_id(3, 2), nmos::fields::nc::manufacturer, value::string(U("NcManufacturer")), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Product descriptor")), details::make_nc_property_id(3, 3), nmos::fields::nc::product, value::string(U("NcProduct")), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Serial number")), details::make_nc_property_id(3, 4), nmos::fields::nc::serial_number, value::string(U("NcString")), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Asset tracking identifier (user specified)")), details::make_nc_property_id(3, 5), nmos::fields::nc::user_inventory_code, value::string(U("NcString")), false, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Name of this device in the application. Instance name, not product name")), details::make_nc_property_id(3, 6), nmos::fields::nc::device_name, value::string(U("NcString")), false, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Role of this device in the application")), details::make_nc_property_id(3, 7), nmos::fields::nc::device_role, value::string(U("NcString")), false, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Device operational state")), details::make_nc_property_id(3, 8), nmos::fields::nc::operational_state, value::string(U("NcDeviceOperationalState")), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Reason for most recent reset")), details::make_nc_property_id(3, 9), nmos::fields::nc::reset_cause, value::string(U("NcResetCause")), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Arbitrary message from dev to controller")), details::make_nc_property_id(3, 10), nmos::fields::nc::message, value::string(U("NcString")), true, true, false, false, value::null())); + auto methods = value::array(); + auto events = value::array(); + + web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcDeviceManager class descriptor")), details::make_nc_class_id({ 1, 3, 1 }), U("NcDeviceManager"), value::string(U("DeviceManager")), properties, methods, events)); + } + + // NcClassManager control class + { + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Descriptions of all control classes in the device (descriptors do not contain inherited elements)")), details::make_nc_property_id(3, 1), nmos::fields::nc::control_classes, value::string(U("NcClassDescriptor")), true, false, true, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Descriptions of all data types in the device (descriptors do not contain inherited elements)")), details::make_nc_property_id(3, 2), nmos::fields::nc::datatypes, value::string(U("NcDatatypeDescriptor")), true, false, true, false, value::null())); + auto methods = value::array(); + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("class ID")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("If set the descriptor would contain all inherited elements")), nmos::fields::nc::include_inherited, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Get a single class descriptor")), details::make_nc_method_id(3, 1), U("GetControlClass"), U("NcMethodResultClassDescriptor"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("name of datatype")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("If set the descriptor would contain all inherited elements")), nmos::fields::nc::include_inherited, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Get a single datatype descriptor")), details::make_nc_method_id(3, 2), U("GetDatatype"), U("NcMethodResultDatatypeDescriptor"), parameters, false)); + } + auto events = value::array(); + + web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcClassManager class descriptor")), details::make_nc_class_id({ 1, 3, 2 }), U("NcClassManager"), value::string(U("ClassManager")), properties, methods, events)); + } + + // load the minimal datatypes + data[nmos::fields::nc::datatypes] = value::array(); + auto& datatypes = data[nmos::fields::nc::datatypes]; + + // NcObject datatypes + // NcClassId + web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Sequence of class ID fields")), U("NcClassId"), value::null(), U("NcInt32"), true)); + // NcOid + web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Object id")), U("NcOid"), value::null(), U("NcUint32"), false)); + // NcTouchpoint + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Context namespace")), nmos::fields::nc::context_namespace, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Base touchpoint class")), U("NcTouchpoint"), value::null(), fields, value::null())); + } + // NcElementId + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Level of the element")), nmos::fields::nc::level, value::string(U("NcUint16")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Index of the element")), nmos::fields::nc::index, value::string(U("NcUint16")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Class element id which contains the level and index")), U("NcElementId"), value::null(), fields, value::null())); + } + // NcPropertyId + { + auto fields = value::array(); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Property id which contains the level and index")), U("NcPropertyId"), value::null(), fields, value::string(U("NcElementId")))); + } + // NcPropertyConstraints + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("The id of the property being constrained")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional default value")), nmos::fields::nc::default_value, value::null(), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Property constraints class")), U("NcPropertyConstraints"), value::null(), fields, value::null())); + } + // NcMethodResultPropertyValue + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Getter method value for the associated property")), nmos::fields::nc::value, value::null(), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Result when invoking the getter method associated with a property")), U("NcMethodResultPropertyValue"), value::null(), fields, value::string(U("NcMethodResult")))); + } + // NcMethodStatus + { + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful")), U("Ok"), 200)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful but targeted property is deprecated")), U("PropertyDeprecated"), 298)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful but method is deprecated")), U("MethodDeprecated"), 299)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Badly-formed command (e.g. the incoming command has invalid message encoding and cannot be parsed by the underlying protocol)")), U("BadCommandFormat"), 400)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Client is not authorized")), U("Unauthorized"), 401)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Command addresses a nonexistent object")), U("BadOid"), 404)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Attempt to change read-only state")), U("Readonly"), 405)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call is invalid in current operating context (e.g. attempting to invoke a method when the object is disabled)")), U("InvalidRequest"), 406)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("There is a conflict with the current state of the device")), U("Conflict"), 409)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Something was too big")), U("BufferOverflow"), 413)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Index is outside the available range")), U("IndexOutOfBounds"), 414)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method parameter does not meet expectations (e.g. attempting to invoke a method with an invalid type for one of its parameters)")), U("ParameterError"), 417)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed object is locked")), U("Locked"), 423)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Internal device error")), U("DeviceError"), 500)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed method is not implemented by the addressed object")), U("MethodNotImplemented"), 501)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed property is not implemented by the addressed object")), U("PropertyNotImplemented"), 502)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("The device is not ready to handle any commands")), U("NotReady"), 503)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call did not finish within the allotted time")), U("Timeout"), 504)); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Method invokation status")), U("NcMethodStatus"), value::null(), items)); + } + // NcMethodResult + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Status for the invoked method")), nmos::fields::nc::status, value::string(U("NcMethodStatus")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Base result of the invoked method")), U("NcMethodResult"), value::null(), fields, value::null())); + } + // NcId + web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Identity handler")), U("NcId"), value::null(), U("NcUint32"), false)); + // NcMethodResultId + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Id result value")), nmos::fields::nc::value, value::string(U("NcId")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Id method result")), U("NcMethodResultId"), value::null(), fields, value::string(U("NcMethodResult")))); + } + // NcMethodResultLength + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Length result value")), nmos::fields::nc::value, value::string(U("NcUint32")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Length method result")), U("NcMethodResultLength"), value::null(), fields, value::string(U("NcMethodResult")))); + } + // NcPropertyChangeType + { + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Current value changed")), U("ValueChanged"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item added")), U("SequenceItemAdded"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item changed")), U("SequenceItemChanged"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item removed")), U("SequenceItemRemoved"), 3)); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Type of property change")), U("NcPropertyChangeType"), value::null(), items)); + } + // NcPropertyChangedEventData + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("The id of the property that changed")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Information regarding the change type")), nmos::fields::nc::change_type, value::string(U("NcPropertyChangeType")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property-type specific value")), nmos::fields::nc::value, value::null(), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Index of sequence item if the property is a sequence")), nmos::fields::nc::sequence_item_index, value::string(U("NcId")), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Payload of property-changed event")), U("NcPropertyChangedEventData"), value::null(), fields, value::null())); + } + + // NcBlock datatypes + // NcDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional user facing description")), nmos::fields::nc::description, value::string(U("NcString")), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Base descriptor")), U("NcDescriptor"), value::null(), fields, value::null())); + } + // NcBlockMemberDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Role of member in its containing block")), nmos::fields::nc::role, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("OID of member")), nmos::fields::nc::oid, value::string(U("NcOid")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff member's OID is hardwired into device")), nmos::fields::nc::constant_oid, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Class ID")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("User label")), nmos::fields::nc::user_label, value::string(U("NcString")), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Containing block's OID")), nmos::fields::nc::owner, value::string(U("NcOid")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor which is specific to a block member")), U("NcBlockMemberDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); + } + // NcMethodResultBlockMemberDescriptors + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Block member descriptors method result value")), nmos::fields::nc::value, value::string(U("NcBlockMemberDescriptor")), false, true, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing block member descriptors as the value")), U("NcMethodResultBlockMemberDescriptors"), value::null(), fields, value::string(U("NcMethodResult")))); + } + + // NcWorker has no datatypes + + // NcManager has no datatypes + + // NcDeviceManager datatypes + // NcVersionCode + web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Version code in semantic versioning format")), U("NcVersionCode"), value::null(), U("NcString"), false)); + // NcOrganizationId + web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Unique 24-bit organization id")), U("NcOrganizationId"), value::null(), U("NcInt32"), false)); + // NcUri + web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Uniform resource identifier")), U("NcUri"), value::null(), U("NcString"), false)); + // NcManufacturer + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's name")), nmos::fields::nc::name, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("IEEE OUI or CID of manufacturer")), nmos::fields::nc::organization_id, value::string(U("NcOrganizationId")), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("URL of the manufacturer's website")), nmos::fields::nc::website, value::string(U("NcUri")), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Manufacturer descriptor")), U("NcManufacturer"), value::null(), fields, value::null())); + } + // NcUuid + web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("UUID")), U("NcUuid"), value::null(), U("NcString"), false)); + // NcProduct + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Product name")), nmos::fields::nc::name, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's unique key to product - model number, SKU, etc")), nmos::fields::nc::key, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's product revision level code")), nmos::fields::nc::revision_level, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Brand name under which product is sold")), nmos::fields::nc::brand_name, value::string(U("NcString")), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Unique UUID of product (not product instance)")), nmos::fields::nc::uuid, value::string(U("NcUuid")), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Text description of product")), nmos::fields::nc::description, value::string(U("NcString")), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Product descriptor")), U("NcProduct"), value::null(), fields, value::null())); + } + // NcDeviceGenericState + { + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Normal operation")), U("NormalOperation"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is initializing")), U("Initializing"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is performing a software or firmware update")), U("Updating"), 3)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is experiencing a licensing error")), U("LicensingError"), 4)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is experiencing an internal error")), U("InternalError"), 5)); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Device generic operational state")), U("NcDeviceGenericState"), value::null(), items)); + } + // NcDeviceOperationalState + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Generic operational state")), nmos::fields::nc::generic_state, value::string(U("NcDeviceGenericState")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Specific device details")), nmos::fields::nc::device_specific_details, value::string(U("NcString")), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Device operational state")), U("NcDeviceOperationalState"), value::null(), fields, value::null())); + } + // NcResetCause + { + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Power on")), U("PowerOn"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Internal error")), U("InternalError"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Upgrade")), U("Upgrade"), 3)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Controller request")), U("ControllerRequest"), 4)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Manual request from the front panel")), U("ManualReset"), 5)); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Reset cause enum")), U("NcResetCause"), value::null(), items)); + } + + // NcClassManager datatypes + // NcName + web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Programmatically significant name, alphanumerics + underscore, no spaces")), U("NcName"), value::null(), U("NcString"), false)); + // NcPropertyDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property id with level and index")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of property")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of property's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is read-only")), nmos::fields::nc::is_read_only, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class property")), U("NcPropertyDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); + } + // NcMethodId + { + auto fields = value::array(); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Method id which contains the level and index")), U("NcMethodId"), value::null(), fields, value::string(U("NcElementId")))); + } + // NcParameterDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of parameter")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of parameter's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a method parameter")), U("NcParameterDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); + } + // NcMethodDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Method id with level and index")), nmos::fields::nc::id, value::string(U("NcMethodId")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of method")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of method result's datatype")), nmos::fields::nc::result_datatype, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Parameter descriptors if any")), nmos::fields::nc::parameters, value::string(U("NcParameterDescriptor")), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class method")), U("NcMethodDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); + } + // NcEventId + { + auto fields = value::array(); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Event id which contains the level and index")), U("NcEventId"), value::null(), fields, value::string(U("NcElementId")))); + } + // NcEventDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Event id with level and index")), nmos::fields::nc::id, value::string(U("NcEventId")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of event")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of event data's datatype")), nmos::fields::nc::event_datatype, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class event")), U("NcEventDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); + } + // NcClassDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Identity of the class")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of the class")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Role if the class has fixed role (manager classes)")), nmos::fields::nc::fixed_role, value::string(U("NcString")), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property descriptors")), nmos::fields::nc::properties, value::string(U("NcPropertyDescriptor")), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Method descriptors")), nmos::fields::nc::methods, value::string(U("NcMethodDescriptor")), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Event descriptors")), nmos::fields::nc::events, value::string(U("NcEventDescriptor")), false, true, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class")), U("NcClassDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); + } + // NcParameterConstraints + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Default value")), nmos::fields::nc::default_value, value::null(), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Abstract parameter constraints class")), U("NcParameterConstraints"), value::null(), fields, value::null())); + } + // NcDatatypeType + { + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Primitive datatype")), U("Primitive"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Simple alias of another datatype")), U("Typedef"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Data structure")), U("Struct"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Enum datatype")), U("Enum"), 3)); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Datatype type")), U("NcDatatypeType"), value::null(), items)); + } + // NcDatatypeDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Datatype name")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Type: Primitive, Typedef, Struct, Enum")), nmos::fields::nc::type, value::string(U("NcDatatypeType")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Base datatype descriptor")), U("NcDatatypeDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); + } + // NcMethodResultClassDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Class descriptor method result value")), nmos::fields::nc::value, value::string(U("NcClassDescriptor")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing a class descriptor as the value")), U("NcMethodResultClassDescriptor"), value::null(), fields, value::string(U("NcMethodResult")))); + } + // NcMethodResultDatatypeDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Datatype descriptor method result value")), nmos::fields::nc::value, value::string(U("NcDatatypeDescriptor")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing a datatype descriptor as the value")), U("NcMethodResultDatatypeDescriptor"), value::null(), fields, value::string(U("NcMethodResult")))); + } + + return data; + } + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + nmos::resource make_device_manager(details::nc_oid oid, nmos::resource& root_block, const nmos::settings& settings) + { + using web::json::value; + + auto& root_block_data = root_block.data; + const auto& owner = nmos::fields::nc::oid(root_block_data); + const auto user_label = value::string(U("Device manager")); + const auto description = value::string(U("The device manager offers information about the product this device is representing")); + const auto& manufacturer = details::make_nc_manufacturer(nmos::experimental::fields::manufacturer_name(settings)); + const auto& product = details::make_nc_product(nmos::experimental::fields::product_name(settings), nmos::experimental::fields::product_key(settings), nmos::experimental::fields::product_key(settings)); + const auto& serial_number = nmos::experimental::fields::serial_number(settings); + const auto device_name = value::null(); + const auto device_role = value::null(); + const auto& operational_state = details::make_nc_device_operational_state(details::nc_device_generic_state::NormalOperation, value::null()); + + auto data = details::make_nc_device_manager(oid, owner, user_label, value::null(), value::null(), + manufacturer, product, serial_number, value::null(), device_name, device_role, operational_state, details::nc_reset_cause::Unknown); + + // add NcDeviceManager block_member_descriptor to root block members + web::json::push_back(root_block_data[nmos::fields::nc::members], details::make_nc_block_member_descriptor( + description, nmos::fields::nc::role(data), oid, nmos::fields::nc::constant_oid(data), data.at(nmos::fields::nc::class_id), user_label, owner)); + + return{ is12_versions::v1_0, types::nc_device_manager, std::move(data), true }; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + nmos::resource make_class_manager(details::nc_oid oid, nmos::resource& root_block) + { + using web::json::value; + + auto& root_block_data = root_block.data; + const auto& owner = nmos::fields::nc::oid(root_block_data); + const auto user_label = value::string(U("Class manager")); + const auto description = value::string(U("The class manager offers access to control class and data type descriptors")); + + auto data = details::make_nc_class_manager(oid, owner, user_label); + + // add NcClassManager block_member_descriptor to root block members + web::json::push_back(root_block_data[nmos::fields::nc::members], details::make_nc_block_member_descriptor( + description, nmos::fields::nc::role(data), oid, nmos::fields::nc::constant_oid(data), data.at(nmos::fields::nc::class_id), user_label, owner)); + + return{ is12_versions::v1_0, types::nc_class_manager, std::move(data), true }; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + nmos::resource make_root_block() + { + using web::json::value; + + auto data = details::make_nc_block(1, true, value::null(), U("root"), value::string(U("Root")), value::null(), value::null(), true, value::array()); + + return{ is12_versions::v1_0, types::nc_block, std::move(data), true }; + } +} diff --git a/Development/nmos/control_protocol_resources.h b/Development/nmos/control_protocol_resources.h new file mode 100644 index 000000000..f7683c2ae --- /dev/null +++ b/Development/nmos/control_protocol_resources.h @@ -0,0 +1,147 @@ +#ifndef NMOS_CONTROL_PROTOCOL_RESOURCES_H +#define NMOS_CONTROL_PROTOCOL_RESOURCES_H + +#include +#include "nmos/settings.h" + +namespace web +{ + namespace json + { + class value; + } +} + +namespace nmos +{ + struct resource; + + namespace details + { + namespace nc_message_type + { + enum type + { + command = 0, + command_response = 1, + notification = 2, + subscription = 3, + subscription_response = 4, + error = 5 + }; + } + + // Method invokation status + namespace nc_method_status + { + enum status + { + ok = 200, // Method call was successful + property_deprecated = 298, // Method call was successful but targeted property is deprecated + method_deprecated = 299, // Method call was successful but method is deprecated + bad_command_format = 400, // Badly-formed command + unathorized = 401, // Client is not authorized + bad_oid = 404, // Command addresses a nonexistent object + read_only = 405, // Attempt to change read-only state + invalid_request = 406, // Method call is invalid in current operating context + conflict = 409, // There is a conflict with the current state of the device + buffer_overflow = 413, // Something was too big + parameter_error = 417, // Method parameter does not meet expectations + locked = 423, // Addressed object is locked + device_error = 500, // Internal device error + method_not_implemented = 501, // Addressed method is not implemented by the addressed object + property_not_implemented = 502, // Addressed property is not implemented by the addressed object + not_ready = 503, // The device is not ready to handle any commands + timeout = 504, // Method call did not finish within the allotted time + property_version_error = 505 // Incompatible protocol version + }; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodresult + struct nc_method_result + { + nc_method_status::status status; + }; + + // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-response-message-type + web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message); + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result); + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value); + + // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-message-type + web::json::value make_control_protocol_message_response(nc_message_type::type type, const web::json::value& responses); + // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#error-messages + web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message); + + // Datatype type + namespace nc_datatype_type + { + enum type + { + Primitive = 0, + Typedef = 1, + Struct = 2, + Enum = 3 + }; + } + + // Device generic operational state + namespace nc_device_generic_state + { + enum state + { + Unknown = 0, // Unknown + NormalOperation = 1, // Normal operation + Initializing = 2, // Device is initializing + Updating = 3, // Device is performing a software or firmware update + LicensingError = 4, // Device is experiencing a licensing error + InternalError = 5 // Device is experiencing an internal error + }; + } + + // Reset cause enum + namespace nc_reset_cause + { + enum cause + { + Unknown = 0, // 0 Unknown + Power_on = 1, // 1 Power on + InternalError = 2, // 2 Internal error + Upgrade = 3, // 3 Upgrade + Controller_request = 4, // 4 Controller request + ManualReset = 5 // 5 Manual request from the front panel + }; + } + + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncid + typedef uint32_t nc_id; + + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncoid + typedef uint32_t nc_oid; + + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncuri + typedef utility::string_t nc_uri; + + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncuuid + typedef utility::string_t nc_uuid; + + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid + typedef std::vector nc_class_id; + + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nctouchpoint + typedef utility::string_t nc_touch_point; + + typedef std::map properties; + + typedef std::function method; + typedef std::map methods; // method_id vs method handler + } + + nmos::resource make_device_manager(details::nc_oid oid, nmos::resource& root_block, const nmos::settings& settings); + + nmos::resource make_class_manager(details::nc_oid oid, nmos::resource& root_block); + + nmos::resource make_root_block(); +} + +#endif diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 89e11721a..6bb263468 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -1,10 +1,52 @@ #include "nmos/control_protocol_ws_api.h" +#include +#include "cpprest/json_validator.h" +#include "cpprest/regex_utils.h" +#include "nmos/api_utils.h" +#include "nmos/control_protocol_resources.h" +#include "nmos/is12_versions.h" +#include "nmos/json_schema.h" #include "nmos/model.h" +#include "nmos/query_utils.h" #include "nmos/slog.h" +#include "nmos/resources.h" namespace nmos { + namespace details + { + static const web::json::experimental::json_validator& controlprotocol_validator() + { + // hmm, could be based on supported API versions from settings, like other APIs' validators? + static const web::json::experimental::json_validator validator + { + nmos::experimental::load_json_schema, + boost::copy_range>(boost::join(boost::join( + is12_versions::all | boost::adaptors::transformed(experimental::make_controlprotocolapi_base_message_schema_uri), + is12_versions::all | boost::adaptors::transformed(experimental::make_controlprotocolapi_command_message_schema_uri)), + is12_versions::all | boost::adaptors::transformed(experimental::make_controlprotocolapi_subscription_message_schema_uri) + )) + }; + return validator; + } + + // Validate against specification schema + // throws web::json::json_exception on failure, which results in a 400 Badly-formed command + void validate_controlprotocolapi_base_message_schema(const nmos::api_version& version, const web::json::value& request_data) + { + controlprotocol_validator().validate(request_data, experimental::make_controlprotocolapi_base_message_schema_uri(version)); + } + void validate_controlprotocolapi_command_message_schema(const nmos::api_version& version, const web::json::value& request_data) + { + controlprotocol_validator().validate(request_data, experimental::make_controlprotocolapi_command_message_schema_uri(version)); + } + void validate_controlprotocolapi_subscription_message_schema(const nmos::api_version& version, const web::json::value& request_data) + { + controlprotocol_validator().validate(request_data, experimental::make_controlprotocolapi_subscription_message_schema_uri(version)); + } + } + // IS-12 Control Protocol WebSocket API web::websockets::experimental::listener::validate_handler make_control_protocol_ws_validate_handler(nmos::node_model& model, slog::base_gate& gate_) @@ -28,20 +70,72 @@ namespace nmos web::websockets::experimental::listener::open_handler make_control_protocol_ws_open_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate_) { + using web::json::value; + using web::json::value_of; + return [&model, &websockets, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id) { nmos::ws_api_gate gate(gate_, connection_uri); auto lock = model.write_lock(); + auto& resources = model.control_protocol_resources; const auto& ws_ncp_path = connection_uri.path(); slog::log(gate, SLOG_FLF) << "Opening websocket connection to: " << ws_ncp_path; + + // create a subscription (1-1 relationship with the connection) + resources::const_iterator subscription; + + { + const bool secure = nmos::experimental::fields::client_secure(model.settings); + + const auto ws_href = web::uri_builder() + .set_scheme(web::ws_scheme(secure)) + .set_host(nmos::get_host(model.settings)) + .set_port(nmos::fields::events_ws_port(model.settings)) + .set_path(ws_ncp_path) + .to_uri(); + + const bool non_persistent = false; + value data = value_of({ + { nmos::fields::id, nmos::make_id() }, + { nmos::fields::max_update_rate_ms, 0 }, + { nmos::fields::resource_path, U('/') + nmos::resourceType_from_type(nmos::types::source) }, + { nmos::fields::params, value_of({ { U("query.rql"), U("in(id,())") } }) }, + { nmos::fields::persist, non_persistent }, + { nmos::fields::secure, secure }, + { nmos::fields::ws_href, ws_href.to_string() } + }, true); + + // hm, could version be determined from ws_resource_path? + nmos::resource subscription_{ is12_versions::v1_0, nmos::types::subscription, std::move(data), non_persistent }; + + subscription = insert_resource(resources, std::move(subscription_)).first; + } + { // create a websocket connection resource + value data; nmos::id id = nmos::make_id(); + data[nmos::fields::id] = value::string(id); + data[nmos::fields::subscription_id] = value::string(subscription->id); + + // create an initial websocket message with no data + + const auto resource_path = nmos::fields::resource_path(subscription->data); + const auto topic = resource_path + U('/'); + // source_id and flow_id are set per-message depending on the source, unlike Query WebSocket API + data[nmos::fields::message] = details::make_grain({}, {}, topic); + + resource grain{ is12_versions::v1_0, nmos::types::grain, std::move(data), false }; + insert_resource(resources, std::move(grain)); + websockets.insert({ id, connection_id }); slog::log(gate, SLOG_FLF) << "Creating websocket connection: " << id; + + slog::log(gate, SLOG_FLF) << "Notifying control protocol websockets thread"; // and anyone else who cares... + model.notify(); } }; } @@ -52,6 +146,7 @@ namespace nmos { nmos::ws_api_gate gate(gate_, connection_uri); auto lock = model.write_lock(); + auto& resources = model.control_protocol_resources; const auto& ws_ncp_path = connection_uri.path(); slog::log(gate, SLOG_FLF) << "Closing websocket connection to: " << ws_ncp_path << " [" << (int)close_status << ": " << close_reason << "]"; @@ -59,31 +154,583 @@ namespace nmos auto websocket = websockets.right.find(connection_id); if (websockets.right.end() != websocket) { - slog::log(gate, SLOG_FLF) << "Deleting websocket connection"; + auto grain = find_resource(resources, { websocket->second, nmos::types::grain }); + + if (resources.end() != grain) + { + slog::log(gate, SLOG_FLF) << "Deleting websocket connection"; + + // subscriptions have a 1-1 relationship with the websocket connection and both should now be erased immediately + auto subscription = find_resource(resources, { nmos::fields::subscription_id(grain->data), nmos::types::subscription }); + + if (resources.end() != subscription) + { + // this should erase grain too, as a subscription's subresource + erase_resource(resources, subscription->id); + } + else + { + // a grain without a subscription shouldn't be possible, but let's be tidy + erase_resource(resources, grain->id); + } + //erase_resource(resources, grain->id); + } websockets.right.erase(websocket); + + model.notify(); } }; } web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate_) { - return [&model, &websockets, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id, const web::websockets::websocket_incoming_message& msg_) + using web::json::value; + using web::json::value_of; + + // NcObject properties + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject + const details::properties nc_object_properties = + { + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject + { value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 1 } }), nmos::fields::nc::class_id }, + { value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 2 } }), nmos::fields::nc::oid }, + { value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 3 } }), nmos::fields::nc::constant_oid }, + { value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 4 } }), nmos::fields::nc::owner }, + { value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 5 } }), nmos::fields::nc::role }, + { value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 6 } }), nmos::fields::nc::user_label }, + { value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 7 } }), nmos::fields::nc::touchpoints }, + { value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 8 } }), nmos::fields::nc::runtime_property_constraints } + }; + + // NcBlock properties + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + const details::properties nc_block_properties = + { + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + { value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 1 } }), nmos::fields::nc::enabled }, + { value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 2 } }), nmos::fields::nc::members } + }; + + // NcWorker properties + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker + const details::properties nc_worker_properties = + { + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker + { value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 1 } }), nmos::fields::nc::enabled } + }; + + // NcManager has no property + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager + const details::properties nc_manager_properties; + + // NcDeviceManager properties + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + const details::properties nc_device_manager_properties = + { + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 1 } }), nmos::fields::nc::nc_version }, + { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 2 } }), nmos::fields::nc::manufacturer }, + { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 3 } }), nmos::fields::nc::product }, + { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 4 } }), nmos::fields::nc::serial_number }, + { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 5 } }), nmos::fields::nc::user_inventory_code }, + { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 6 } }), nmos::fields::nc::device_name }, + { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 7 } }), nmos::fields::nc::device_role }, + { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 8 } }), nmos::fields::nc::operational_state }, + { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 9 } }), nmos::fields::nc::reset_cause }, + { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 10 } }), nmos::fields::nc::message } + }; + + // NcClassManager properties + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + const details::properties nc_class_manager_properties = + { + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 1 } }), nmos::fields::nc::control_classes }, + { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 2 } }), nmos::fields::nc::datatypes } + }; + + // method handlers for the different classes + details::methods nc_object_method_handlers; // method_id vs NcObject method_handler + details::methods nc_block_method_handlers; // method_id vs NcBlock method_handler + details::methods nc_worker_method_handlers; // method_id vs NcWorker method_handler + details::methods nc_manager_method_handlers; // method_id vs NcManager method_handler + details::methods nc_device_manager_method_handlers; // method_id vs NcDeviceManager method_handler + details::methods nc_class_manager_method_handlers; // method_id vs NcClassManager method_handler + + // NcObject methods implementation + // get property + auto get = [&model](const details::properties& properties, int32_t handle, int32_t oid, const value& arguments) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto& resources = model.control_protocol_resources; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + // where arguments is the property id = (level, index) + const auto& property_id = nmos::fields::nc::id(arguments); + + // is property_id defined in properties map + auto property_found = properties.find(property_id); + if (property_found != properties.end()) + { + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, resource->data.at(property_found->second)); + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << " to do get"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + } + + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid << " to do get"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + }; + // set property + auto set = [&model](const details::properties& properties, int32_t handle, int32_t oid, const value& arguments) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto& resources = model.control_protocol_resources; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& val = nmos::fields::nc::value(arguments); + + // hmm, todo check property_id allowed in resource's class_id + + // is property_id defined in properties map + auto property_found = properties.find(property_id); + if (property_found != properties.end()) + { + resources.modify(resource, [&](nmos::resource& resource) + { + resource.data[property_found->second] = val; + + resource.updated = strictly_increasing_update(resources); + }); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); + } + else + { + // hmm, find property function from user properties map + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << " to do set"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + } + + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid << " to do set"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + }; + + // NcBlock methods implementation + // get descriptors of members of the block + auto get_member_descriptors = [&model](const details::properties& properties, int32_t handle, int32_t oid, const value& arguments) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto& resources = model.control_protocol_resources; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + // where arguments is the boolean recurse value + // hmm, If recurse is set to true, nested members is to be retrieved + const auto& recurse = nmos::fields::nc::recurse(arguments); + + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, resource->data.at(nmos::fields::nc::members)); + } + + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid << " to get member descriptors"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + }; + + // NcClassManager methods implementation + auto get_control_class = [&model](const details::properties& properties, int32_t handle, int32_t oid, const value& arguments) + { + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid << " to get control class"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + }; + + // NcObject methods + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject + nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 1 } })] = get; + nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 2 } })] = set; + //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 3 } })] = get_sequence_item; + //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 4 } })] = set_sequence_item; + //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 5 } })] = add_sequence_item; + //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 6 } })] = remove_sequence_item; + //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 7 } })] = get_sequence_length; + + // NcBlock methods + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 1 } })] = get_member_descriptors; + //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 2 } })] = find_members_by_path; + //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 3 } })] = find_members_by_role; + //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 4 } })] = find_members_by_class_id; + + // NcWorker has no extended method + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker + + // NcManager has no extended method + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager + + // NcDeviceManger has no extended method + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + + // NcClassManager methods + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 1 } })] = get_control_class; + //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 2 } })] = get_datatype; + + // create properties and method handlers based on resource type + auto create_properties_methods = [=](const nmos::type& type) + { + details::properties properties; + details::methods methods; + + // all start from NcObject + properties.insert(nc_object_properties.begin(), nc_object_properties.end()); + methods.insert(nc_object_method_handlers.begin(), nc_object_method_handlers.end()); + if (type == nmos::types::nc_block) + { + properties.insert(nc_block_properties.begin(), nc_block_properties.end()); + methods.insert(nc_block_method_handlers.begin(), nc_block_method_handlers.end()); + } + else if (type == nmos::types::nc_worker) + { + properties.insert(nc_worker_properties.begin(), nc_worker_properties.end()); + methods.insert(nc_worker_method_handlers.begin(), nc_worker_method_handlers.end()); + } + else if (type == nmos::types::nc_manager) + { + properties.insert(nc_manager_properties.begin(), nc_manager_properties.end()); + methods.insert(nc_manager_method_handlers.begin(), nc_manager_method_handlers.end()); + } + else if (type == nmos::types::nc_device_manager) + { + properties.insert(nc_manager_properties.begin(), nc_manager_properties.end()); + properties.insert(nc_device_manager_properties.begin(), nc_device_manager_properties.end()); + methods.insert(nc_manager_method_handlers.begin(), nc_manager_method_handlers.end()); + methods.insert(nc_device_manager_method_handlers.begin(), nc_device_manager_method_handlers.end()); + } + else if (type == nmos::types::nc_class_manager) + { + properties.insert(nc_manager_properties.begin(), nc_manager_properties.end()); + properties.insert(nc_class_manager_properties.begin(), nc_class_manager_properties.end()); + methods.insert(nc_manager_method_handlers.begin(), nc_manager_method_handlers.end()); + methods.insert(nc_class_manager_method_handlers.begin(), nc_class_manager_method_handlers.end()); + } + + // hmm, add user properties + //if (!user_properties.empty()) + //{ + // properties.insert(user_properties.begin(), user_properties.end()); + //} + + // hmm, add user method handlers + //if (!user_methods.empty()) + //{ + // methods.insert(user_methods.begin(), user_methods.end()); + //} + + return std::pair(properties, methods); + }; + + return [&model, &websockets, create_properties_methods, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id, const web::websockets::websocket_incoming_message& msg_) { nmos::ws_api_gate gate(gate_, connection_uri); - auto lock = model.read_lock(); + + auto lock = model.write_lock(); + auto& resources = model.control_protocol_resources; // theoretically blocking, but in fact not auto msg = msg_.extract_string().get(); const auto& ws_ncp_path = connection_uri.path(); - slog::log(gate, SLOG_FLF) << "Received websocket message: " << msg << " on connection to: " << ws_ncp_path; + slog::log(gate, SLOG_FLF) << "Received websocket message: " << msg << " on connection: " << ws_ncp_path; + + // hmm todo: extract the version from the ws_ncp_path + const nmos::api_version version = is12_versions::v1_0; + //const nmos::api_version version = nmos::parse_api_version(ws_ncp_path(nmos::patterns::version.name)); auto websocket = websockets.right.find(connection_id); if (websockets.right.end() != websocket) { - // hmm, todo message handling.... + auto grain = find_resource(resources, { websocket->second, nmos::types::grain }); + + if (resources.end() != grain) + { + auto subscription = find_resource(resources, { nmos::fields::subscription_id(grain->data), nmos::types::subscription }); + + if (resources.end() != subscription) + { + try + { + const auto message = value::parse(utility::conversions::to_string_t(msg)); + + // validate the base-message + details::validate_controlprotocolapi_base_message_schema(version, message); + + const auto msg_type = nmos::fields::nc::message_type(message); + switch (msg_type) + { + case details::nc_message_type::command: + { + // validate command-message + details::validate_controlprotocolapi_command_message_schema(version, message); + + auto responses = value::array(); + auto& commands = nmos::fields::nc::commands(message); + for (const auto& cmd : commands) + { + const auto handle = nmos::fields::nc::handle(cmd); + const auto oid = nmos::fields::nc::oid(cmd); + + // get methodId + const auto& method_id = nmos::fields::nc::method_id(cmd); + + // get arguments + const auto& arguments = nmos::fields::nc::arguments(cmd); + + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + // create properties and method handlers based on resource type + auto properties_methods = create_properties_methods(resource->type); + auto& properties = properties_methods.first; + auto& methods = properties_methods.second; + + // find the relevent method handler to execute + auto method = methods.find(method_id); + if (method != methods.end()) + { + // execute the relevant method handler, then accumulating up their response to reponses + web::json::push_back(responses, method->second(properties, handle, oid, arguments)); + } + else + { + utility::stringstream_t ss; + ss << U("unsupported method id: ") << method_id.serialize(); + web::json::push_back(responses, + details::make_control_protocol_error_response(handle, { details::nc_method_status::method_not_implemented }, ss.str())); + } + } + else + { + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid; + web::json::push_back(responses, + details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str())); + } + } + + // add command_response for the control protocol response thread to return to the client + resources.modify(grain, [&](nmos::resource& grain) + { + web::json::push_back(nmos::fields::message_grain_data(grain.data), + details::make_control_protocol_message_response(details::nc_message_type::command_response, responses)); + + grain.updated = strictly_increasing_update(resources); + }); + } + break; + case details::nc_message_type::subscription: + { + // hmm, todo... + } + break; + default: + // unexpected message type + break; + } + + } + catch (const web::json::json_exception& e) + { + slog::log(gate, SLOG_FLF) << "JSON error: " << e.what(); + + resources.modify(grain, [&](nmos::resource& grain) + { + web::json::push_back(nmos::fields::message_grain_data(grain.data), + details::make_control_protocol_error_message({ details::nc_method_status::bad_command_format }, utility::s2us(e.what()))); + + grain.updated = strictly_increasing_update(resources); + }); + } + catch (const std::exception& e) + { + slog::log(gate, SLOG_FLF) << "Unexpected exception while handing control protocol command: " << e.what(); + + resources.modify(grain, [&](nmos::resource& grain) + { + web::json::push_back(nmos::fields::message_grain_data(grain.data), + details::make_control_protocol_error_message({ details::nc_method_status::bad_command_format }, + utility::s2us(std::string("Unexpected exception while handing control protocol command : ") + e.what()))); + + grain.updated = strictly_increasing_update(resources); + }); + } + catch (...) + { + slog::log(gate, SLOG_FLF) << "Unexpected unknown exception for handing control protocol command"; + + resources.modify(grain, [&](nmos::resource& grain) + { + web::json::push_back(nmos::fields::message_grain_data(grain.data), + details::make_control_protocol_error_message({ details::nc_method_status::bad_command_format }, + U("Unexpected unknown exception while handing control protocol command"))); + + grain.updated = strictly_increasing_update(resources); + }); + } + model.notify(); + } + } + } + }; + } + + // observe_websocket_exception is the same as the one defined in events_ws_api + namespace details + { + struct observe_websocket_exception + { + observe_websocket_exception(slog::base_gate& gate) : gate(gate) {} + + void operator()(pplx::task finally) + { + try + { + finally.get(); + } + catch (const web::websockets::websocket_exception& e) + { + slog::log(gate, SLOG_FLF) << "WebSocket error: " << e.what() << " [" << e.error_code() << "]"; + } } + + slog::base_gate& gate; }; } + + void send_control_protocol_ws_messages_thread(web::websockets::experimental::listener::websocket_listener& listener, nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate_) + { + nmos::details::omanip_gate gate(gate_, nmos::stash_category(nmos::categories::send_control_protocol_ws_messages)); + + using web::json::value; + using web::json::value_of; + + // could start out as a shared/read lock, only upgraded to an exclusive/write lock when a grain in the resources is actually modified + auto lock = model.write_lock(); + auto& condition = model.condition; + auto& shutdown = model.shutdown; + auto& resources = model.control_protocol_resources; + + tai most_recent_message{}; + auto earliest_necessary_update = (tai_clock::time_point::max)(); + + for (;;) + { + // wait for the thread to be interrupted either because there are resource changes, or because the server is being shut down + // or because message sending was throttled earlier + details::wait_until(condition, lock, earliest_necessary_update, [&] { return shutdown || most_recent_message < most_recent_update(resources); }); + if (shutdown) break; + most_recent_message = most_recent_update(resources); + + slog::log(gate, SLOG_FLF) << "Got notification on control protocol websockets thread"; + + earliest_necessary_update = (tai_clock::time_point::max)(); + + std::vector> outgoing_messages; + + for (auto wit = websockets.left.begin(); websockets.left.end() != wit;) + { + const auto& websocket = *wit; + + // for each websocket connection that has valid grain and subscription resources + const auto grain = find_resource(resources, { websocket.first, nmos::types::grain }); + if (resources.end() == grain) + { + auto close = listener.close(websocket.second, web::websockets::websocket_close_status::server_terminate, U("Expired")) + .then(details::observe_websocket_exception(gate)); + // theoretically blocking, but in fact not + close.wait(); + + wit = websockets.left.erase(wit); + continue; + } + const auto subscription = find_resource(resources, { nmos::fields::subscription_id(grain->data), nmos::types::subscription }); + if (resources.end() == subscription) + { + // a grain without a subscription shouldn't be possible, but let's be tidy + erase_resource(resources, grain->id); + + auto close = listener.close(websocket.second, web::websockets::websocket_close_status::server_terminate, U("Expired")) + .then(details::observe_websocket_exception(gate)); + // theoretically blocking, but in fact not + close.wait(); + + wit = websockets.left.erase(wit); + continue; + } + // and has events to send + if (0 == nmos::fields::message_grain_data(grain->data).size()) + { + ++wit; + continue; + } + + slog::log(gate, SLOG_FLF) << "Preparing to send " << nmos::fields::message_grain_data(grain->data).size() << " events on websocket connection: " << grain->id; + + for (const auto& event : nmos::fields::message_grain_data(grain->data).as_array()) + { + web::websockets::websocket_outgoing_message message; + + slog::log(gate, SLOG_FLF) << "outgoing_message: " << event.serialize(); + message.set_utf8_message(utility::us2s(event.serialize())); + outgoing_messages.push_back({ websocket.second, message }); + } + + // reset the grain for next time + resources.modify(grain, [&resources](nmos::resource& grain) + { + // all messages have now been prepared + nmos::fields::message_grain_data(grain.data) = value::array(); + grain.updated = strictly_increasing_update(resources); + }); + + ++wit; + } + + // send the messages without the lock on resources + details::reverse_lock_guard unlock{ lock }; + + if (!outgoing_messages.empty()) slog::log(gate, SLOG_FLF) << "Sending " << outgoing_messages.size() << " websocket messages"; + + for (auto& outgoing_message : outgoing_messages) + { + // hmmm, no way to cancel this currently... + + auto send = listener.send(outgoing_message.first, outgoing_message.second) + .then(details::observe_websocket_exception(gate)); + // current websocket_listener implementation is synchronous in any case, but just to make clear... + // for now, wait for the message to be sent + send.wait(); + } + } + } } diff --git a/Development/nmos/control_protocol_ws_api.h b/Development/nmos/control_protocol_ws_api.h index 71772961f..2371616d1 100644 --- a/Development/nmos/control_protocol_ws_api.h +++ b/Development/nmos/control_protocol_ws_api.h @@ -26,6 +26,8 @@ namespace nmos nmos::make_control_protocol_ws_message_handler(model, websockets, gate) }; } + + void send_control_protocol_ws_messages_thread(web::websockets::experimental::listener::websocket_listener& listener, nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate_); } #endif diff --git a/Development/nmos/is12_schemas/is12_schemas.h b/Development/nmos/is12_schemas/is12_schemas.h new file mode 100644 index 000000000..392b57648 --- /dev/null +++ b/Development/nmos/is12_schemas/is12_schemas.h @@ -0,0 +1,25 @@ +#ifndef NMOS_IS12_SCHEMAS_H +#define NMOS_IS12_SCHEMAS_H + +// Extern declarations for auto-generated constants +// could be auto-generated, but isn't currently! +namespace nmos +{ + namespace is12_schemas + { + namespace v1_0_x + { + extern const char* base_message; + extern const char* command_message; + extern const char* command_response_message; + extern const char* error_message; + extern const char* event_data; + extern const char* notification_message; + extern const char* property_changed_event_data; + extern const char* subscription_message; + extern const char* subscription_response_message; + } + } +} + +#endif diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index d04d57923..a18242e7c 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -230,6 +230,94 @@ namespace nmos const web::json::field_as_string hostname{ U("hostname") }; // hostname, ipv4 or ipv6 const web::json::field_as_integer port{ U("port") }; // 1..65535 + // IS-12 Control Protocol + namespace nc + { + // for control_protocol_ws_api + const web::json::field_as_integer message_type{ U("messageType") }; + + // for control_protocol_ws_api commands + const web::json::field_as_array commands{ U("commands") }; + const web::json::field_as_integer oid{ U("oid") }; + const web::json::field_as_value method_id{ U("methodId") }; + const web::json::field_as_value arguments{ U("arguments") }; + const web::json::field_as_value id{ U("id") }; + const web::json::field_as_integer level{ U("level") }; + const web::json::field_as_integer index{ U("index") }; + + // for control_protocol_ws_api responses & errors + const web::json::field_as_value responses{ U("responses") }; + const web::json::field_as_value result{ U("result") }; + const web::json::field_as_integer status{ U("status") }; + const web::json::field_as_value value{ U("value") }; + const web::json::field_as_string error_message{ U("errorMessage") }; + + // for control_protocol_ws_api commands & responses + const web::json::field_as_integer handle{ U("handle") }; + + const web::json::field_as_array class_id{ U("classId") }; + const web::json::field_as_bool constant_oid{ U("constantOid") }; + const web::json::field_as_integer owner{ U("owner") }; + const web::json::field_as_string role{ U("role") }; + const web::json::field_as_string user_label{ U("userLabel") }; + const web::json::field_as_array touchpoints{ U("touchpoints") }; + const web::json::field_as_array runtime_property_constraints{ U("runtimePropertyConstraints") }; + const web::json::field_as_bool recurse{ U("recurse") }; + const web::json::field_as_bool enabled{ U("enabled") }; + const web::json::field_as_array members{ U("members") }; + const web::json::field_as_string description{ U("description") }; // can be null + const web::json::field_as_string nc_version{ U("ncVersion") }; // NcVersionCode, string + const web::json::field_as_value manufacturer{ U("manufacturer") }; // NcManufacturer + const web::json::field_as_value product{ U("product") }; // NcProduct + const web::json::field_as_string serial_number{ U("serialNumber") }; + const web::json::field_as_string user_inventory_code{ U("userInventoryCode") }; // string, can be null + const web::json::field_as_string device_name{ U("deviceName") }; // string, can be null + const web::json::field_as_string device_role{ U("deviceRole") }; // string, can be null + const web::json::field_as_value operational_state{ U("operationalState") }; // NcDeviceOperationalState + const web::json::field_as_integer reset_cause{ U("resetCause") }; // NcResetCause + const web::json::field_as_string message{ U("message") }; // string, can be null + const web::json::field_as_array control_classes{ U("controlClasses") }; // sequence + const web::json::field_as_array datatypes{ U("datatypes") }; // sequence + const web::json::field_as_string name{ U("name")}; + const web::json::field_as_string fixed_role{ U("fixedRole") }; // string, can be null + const web::json::field_as_array properties{ U("properties") }; // sequence + const web::json::field_as_array methods{ U("methods") }; // sequence + const web::json::field_as_array events{ U("events") }; // sequence + const web::json::field_as_integer type{ U("type") }; // NcDatatypeType + const web::json::field_as_value constraints{ U("constraints") }; // NcParameterConstraints, can be null + const web::json::field_as_integer organization_id{ U("organizationId") }; + const web::json::field_as_string website{ U("website") }; + const web::json::field_as_string key{ U("key") }; + const web::json::field_as_string revision_level{ U("revisionLevel") }; + const web::json::field_as_string brand_name{ U("brandName") }; // string, can be null + const web::json::field_as_string uuid{ U("uuid") }; // string, can be null + const web::json::field_as_string type_name{ U("typeName") }; // string, can be null + const web::json::field_as_bool is_read_only{ U("isReadOnly") }; + const web::json::field_as_bool is_persistent{ U("isPersistent") }; + const web::json::field_as_bool is_nullable{ U("isNullable") }; + const web::json::field_as_bool is_sequence{ U("isSequence") }; + const web::json::field_as_bool is_deprecated{ U("isDeprecated") }; + const web::json::field_as_bool is_constant{ U("isConstant") }; // bool, can be null + const web::json::field_as_string parent_type{ U("parentType") }; + const web::json::field_as_string event_datatype{ U("eventDatatype") }; + const web::json::field_as_string result_datatype{ U("resultDatatype") }; + const web::json::field_as_array parameters{ U("parameters") }; + const web::json::field_as_array items{ U("items") }; // sequence + const web::json::field_as_array fields{ U("fields") }; // sequence + const web::json::field_as_integer generic_state{ U("generic") }; // NcDeviceGenericState + const web::json::field_as_string device_specific_details{ U("deviceSpecificDetails") }; // string, can be null + const web::json::field_as_array path{ U("path") }; // NcRolePath + const web::json::field_as_bool case_sensitive{ U("caseSensitive") }; + const web::json::field_as_bool match_whole_string{ U("matchWholeString") }; + const web::json::field_as_bool include_derived{ U("includeDerived") }; + const web::json::field_as_bool include_inherited{ U("includeInherited") }; + const web::json::field_as_string context_namespace{ U("contextNamespace") }; + const web::json::field_as_value default_value{ U("defaultValue") }; + const web::json::field_as_integer change_type{ U("changeType") }; // NcPropertyChangeType + const web::json::field_as_integer sequence_item_index{ U("sequenceItemIndex") }; // NcId, can be null + const web::json::field_as_value property_id{ U("propertyId") }; + } + // NMOS Parameter Registers // Sender Attributes Register diff --git a/Development/nmos/json_schema.cpp b/Development/nmos/json_schema.cpp index fdd70581d..774b9e179 100644 --- a/Development/nmos/json_schema.cpp +++ b/Development/nmos/json_schema.cpp @@ -9,6 +9,8 @@ #include "nmos/is08_schemas/is08_schemas.h" #include "nmos/is09_versions.h" #include "nmos/is09_schemas/is09_schemas.h" +#include "nmos/is12_versions.h" +#include "nmos/is12_schemas/is12_schemas.h" #include "nmos/type.h" namespace nmos @@ -126,6 +128,25 @@ namespace nmos const web::uri systemapi_global_schema_uri = make_schema_uri(tag, _XPLATSTR("global.json")); } } + + namespace is12_schemas + { + web::uri make_schema_uri(const utility::string_t& tag, const utility::string_t& ref = {}) + { + return{ _XPLATSTR("https://github.com/AMWA-TV/is-12/raw/") + tag + _XPLATSTR("/APIs/schemas/") + ref }; + } + + // See https://github.com/AMWA-TV/is-12/tree/v1.0-dev/APIs/schemas/ + namespace v1_0 + { + using namespace nmos::is12_schemas::v1_0_x; + const utility::string_t tag(_XPLATSTR("v1.0.x")); + + const web::uri controlprotocolapi_base_message_schema_uri = make_schema_uri(tag, _XPLATSTR("base-message.json")); + const web::uri controlprotocolapi_command_message_schema_uri = make_schema_uri(tag, _XPLATSTR("command-message.json")); + const web::uri controlprotocolapi_subscription_message_schema_uri = make_schema_uri(tag, _XPLATSTR("subscription-message.json")); + } + } } namespace nmos @@ -310,6 +331,25 @@ namespace nmos }; } + static std::map make_is12_schemas() + { + using namespace nmos::is12_schemas; + + return + { + // v1.0 + { make_schema_uri(v1_0::tag, _XPLATSTR("base-message.json")), make_schema(v1_0::base_message) }, + { make_schema_uri(v1_0::tag, _XPLATSTR("command-message.json")), make_schema(v1_0::command_message) }, + { make_schema_uri(v1_0::tag, _XPLATSTR("command-response-message.json")), make_schema(v1_0::command_response_message) }, + { make_schema_uri(v1_0::tag, _XPLATSTR("error-message.json")), make_schema(v1_0::error_message) }, + { make_schema_uri(v1_0::tag, _XPLATSTR("event-data.json")), make_schema(v1_0::event_data) }, + { make_schema_uri(v1_0::tag, _XPLATSTR("notification-message.json")), make_schema(v1_0::notification_message) }, + { make_schema_uri(v1_0::tag, _XPLATSTR("property-changed-event-data.json")), make_schema(v1_0::property_changed_event_data) }, + { make_schema_uri(v1_0::tag, _XPLATSTR("subscription-message.json")), make_schema(v1_0::subscription_message) }, + { make_schema_uri(v1_0::tag, _XPLATSTR("subscription-response-message.json")), make_schema(v1_0::subscription_response_message) } + }; + } + inline void merge(std::map& to, std::map&& from) { to.insert(from.begin(), from.end()); // std::map::merge in C++17 @@ -321,6 +361,7 @@ namespace nmos merge(result, make_is05_schemas()); merge(result, make_is08_schemas()); merge(result, make_is09_schemas()); + merge(result, make_is12_schemas()); return result; } @@ -382,6 +423,21 @@ namespace nmos return is08_schemas::v1_0::map_activations_post_request_uri; } + web::uri make_controlprotocolapi_base_message_schema_uri(const nmos::api_version& version) + { + return is12_schemas::v1_0::controlprotocolapi_base_message_schema_uri; + } + + web::uri make_controlprotocolapi_command_message_schema_uri(const nmos::api_version& version) + { + return is12_schemas::v1_0::controlprotocolapi_command_message_schema_uri; + } + + web::uri make_controlprotocolapi_subscription_message_schema_uri(const nmos::api_version& version) + { + return is12_schemas::v1_0::controlprotocolapi_subscription_message_schema_uri; + } + // load the json schema for the specified base URI web::json::value load_json_schema(const web::uri& id) { diff --git a/Development/nmos/json_schema.h b/Development/nmos/json_schema.h index e938a513e..e09b3de82 100644 --- a/Development/nmos/json_schema.h +++ b/Development/nmos/json_schema.h @@ -29,6 +29,10 @@ namespace nmos web::uri make_channelmappingapi_map_activations_post_request_schema_uri(const nmos::api_version& version); + web::uri make_controlprotocolapi_base_message_schema_uri(const nmos::api_version& version); + web::uri make_controlprotocolapi_command_message_schema_uri(const nmos::api_version& version); + web::uri make_controlprotocolapi_subscription_message_schema_uri(const nmos::api_version& version); + // load the json schema for the specified base URI web::json::value load_json_schema(const web::uri& id); } diff --git a/Development/nmos/model.h b/Development/nmos/model.h index d5c6b9f99..d9c25559c 100644 --- a/Development/nmos/model.h +++ b/Development/nmos/model.h @@ -101,6 +101,10 @@ namespace nmos // IS-08 inputs and outputs for this node // see nmos/channelmapping_resources.h nmos::resources channelmapping_resources; + + // IS-12 resources for this node + // see nmos/control_protocol_resources.h + nmos::resources control_protocol_resources; }; struct registry_model : model diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index 293784adc..2de24d450 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -94,6 +94,8 @@ namespace nmos size_t event_ws_pos{ 0 }; bool found_event_ws{ false }; + size_t control_protocol_ws_pos{ 0 }; + bool found_control_protocol_ws{ false }; for (auto& ws_handler : node_server.ws_handlers) { // if IP address isn't specified for this router, use default server address or wildcard address @@ -107,9 +109,16 @@ namespace nmos if (ws_handler.first.second == events_ws_port) { found_event_ws = true; } else { ++event_ws_pos; } } + + if (!found_control_protocol_ws) + { + if (ws_handler.first.second == control_protocol_ws_port) { found_control_protocol_ws = true; } + else { ++control_protocol_ws_pos; } + } } auto& events_ws_listener = node_server.ws_listeners.at(event_ws_pos); + auto& control_protocol_ws_listener = node_server.ws_listeners.at(control_protocol_ws_pos); // Set up node operation (including the DNS-SD advertisements) @@ -124,7 +133,8 @@ namespace nmos [&] { nmos::send_events_ws_messages_thread(events_ws_listener, node_model, events_ws_api.second, gate); }, [&] { nmos::erase_expired_events_resources_thread(node_model, gate); }, [&, resolve_auto, set_transportfile, connection_activated] { nmos::connection_activation_thread(node_model, resolve_auto, set_transportfile, connection_activated, gate); }, - [&, channelmapping_activated] { nmos::channelmapping_activation_thread(node_model, channelmapping_activated, gate); } + [&, channelmapping_activated] { nmos::channelmapping_activation_thread(node_model, channelmapping_activated, gate); }, + [&] { nmos::send_control_protocol_ws_messages_thread(control_protocol_ws_listener, node_model, control_protocol_ws_api.second, gate); } }); auto system_changed = node_implementation.system_changed; diff --git a/Development/nmos/settings.h b/Development/nmos/settings.h index 1f240e21f..0790506e3 100644 --- a/Development/nmos/settings.h +++ b/Development/nmos/settings.h @@ -360,6 +360,20 @@ namespace nmos // ocsp_request_max [registry, node]: timeout for interactions with the OCSP server const web::json::field_as_integer_or ocsp_request_max{ U("ocsp_request_max"), 30 }; + + // manufacturer_name [node]: the manufacturer name of the NcDeviceManager used for NMOS Control Protocol + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + const web::json::field_as_string_or manufacturer_name{ U("manufacturer_name"), U("") }; + + // product_name/product_key/product_revision_level [node]: the product description of the NcDeviceManager used for NMOS Control Protocol + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncproduct + const web::json::field_as_string_or product_name{ U("product_name"), U("") }; + const web::json::field_as_string_or product_key{ U("product_key"), U("") }; + const web::json::field_as_string_or product_revision_level{ U("product_revision_level"), U("") }; + + // serial_number [node]: the serial number of the NcDeviceManager used for NMOS Control Protocol + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + const web::json::field_as_string_or serial_number{ U("serial_number"), U("") }; } } } diff --git a/Development/nmos/slog.h b/Development/nmos/slog.h index d3fc150fd..3a50d1059 100644 --- a/Development/nmos/slog.h +++ b/Development/nmos/slog.h @@ -44,6 +44,7 @@ namespace nmos const category send_events_ws_commands{ "send_events_ws_commands" }; const category node_system_behaviour{ "node_system_behaviour" }; const category ocsp_behaviour{ "ocsp_behaviour" }; + const category send_control_protocol_ws_messages{ "send_control_protocol_ws_messages" }; // other categories may be defined ad-hoc } diff --git a/Development/nmos/type.h b/Development/nmos/type.h index d58734f81..4fcf54f97 100644 --- a/Development/nmos/type.h +++ b/Development/nmos/type.h @@ -39,6 +39,13 @@ namespace nmos // the System API global configuration resource type, see nmos/system_resources.h const type global{ U("global") }; + + // the Control Protocol API resource types, see nmos/control_protcol_resources.h + const type nc_block{ U("nc_block") }; + const type nc_worker{ U("nc_worker") }; + const type nc_manager{ U("nc_manager") }; + const type nc_device_manager{ U("nc_device_manager") }; + const type nc_class_manager{ U("nc_class_manager") }; } } diff --git a/Development/third_party/is-12/README.md b/Development/third_party/is-12/README.md new file mode 100644 index 000000000..84e840adb --- /dev/null +++ b/Development/third_party/is-12/README.md @@ -0,0 +1 @@ +This directory is for JSON Schemas diff --git a/Development/third_party/is-12/v1.0.x/APIs/schemas/base-message.json b/Development/third_party/is-12/v1.0.x/APIs/schemas/base-message.json new file mode 100644 index 000000000..1ecd16c6b --- /dev/null +++ b/Development/third_party/is-12/v1.0.x/APIs/schemas/base-message.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Base protocol message structure", + "title": "Base protocol message", + "required": [ + "messageType" + ], + "properties": { + "messageType": { + "description": "Protocol message type", + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5 + ] + } + } +} \ No newline at end of file diff --git a/Development/third_party/is-12/v1.0.x/APIs/schemas/command-message.json b/Development/third_party/is-12/v1.0.x/APIs/schemas/command-message.json new file mode 100644 index 000000000..cce540fa0 --- /dev/null +++ b/Development/third_party/is-12/v1.0.x/APIs/schemas/command-message.json @@ -0,0 +1,79 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Command protocol message structure", + "title": "Command protocol message", + "allOf": [ + { + "$ref": "base-message.json" + }, + { + "type": "object", + "required": [ + "commands", + "messageType" + ], + "properties": { + "commands": { + "description": "Commands being transmited in this transaction", + "type": "array", + "items": { + "type": "object", + "required": [ + "handle", + "oid", + "methodId" + ], + "properties": { + "handle": { + "type": "integer", + "description": "Integer value used for pairing with the response", + "minimum": 1, + "maximum": 65535 + }, + "oid": { + "type": "integer", + "description": "Object id containing the method", + "minimum": 1, + "maximum": 65535 + }, + "methodId": { + "type": "object", + "description": "ID structure for the target method", + "required": [ + "level", + "index" + ], + "properties": { + "level": { + "type": "integer", + "description": "Level component of the method ID", + "minimum": 0, + "maximum": 65535 + }, + "index": { + "type": "integer", + "description": "Index component of the method ID", + "minimum": 1, + "maximum": 65535 + } + } + }, + "arguments": { + "type": "object", + "description": "Method arguments" + } + } + } + }, + "messageType": { + "description": "Protocol message type", + "type": "integer", + "enum": [ + 0 + ] + } + } + } + ] +} \ No newline at end of file diff --git a/Development/third_party/is-12/v1.0.x/APIs/schemas/command-response-message.json b/Development/third_party/is-12/v1.0.x/APIs/schemas/command-response-message.json new file mode 100644 index 000000000..93711f583 --- /dev/null +++ b/Development/third_party/is-12/v1.0.x/APIs/schemas/command-response-message.json @@ -0,0 +1,69 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Command response protocol message structure", + "title": "Command response protocol message", + "allOf": [ + { + "$ref": "base-message.json" + }, + { + "type": "object", + "required": [ + "responses", + "messageType" + ], + "properties": { + "responses": { + "description": "Responses being transmited in this transaction", + "type": "array", + "items": { + "type": "object", + "required": [ + "handle", + "result" + ], + "properties": { + "handle": { + "type": "integer", + "description": "Integer value used for pairing with the command", + "minimum": 1, + "maximum": 65535 + }, + "result": { + "type": "object", + "description": "Response result", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "integer", + "description": "Status of the command response. Must include the numeric values for NcMethodStatus or other types which inherit from it. 200 must be returned if the command was successful", + "minimum": 0, + "maximum": 65535 + }, + "value": { + "type": ["string", "number", "object", "array", "boolean", "null" ], + "description": "Method return value as described in the MS-05-02 Type definition or in a private Type definition" + }, + "errorMessage": { + "description": "Error message associated with the failure of the command (optional)", + "type": "string" + } + } + } + } + } + }, + "messageType": { + "description": "Protocol message type", + "type": "integer", + "enum": [ + 1 + ] + } + } + } + ] +} \ No newline at end of file diff --git a/Development/third_party/is-12/v1.0.x/APIs/schemas/error-message.json b/Development/third_party/is-12/v1.0.x/APIs/schemas/error-message.json new file mode 100644 index 000000000..139c77ffc --- /dev/null +++ b/Development/third_party/is-12/v1.0.x/APIs/schemas/error-message.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Error protocol message structure - used by devices to return general error messages for example when incoming messages do not have messageType, handles or contain invalid JSON", + "title": "Error protocol message", + "allOf": [ + { + "$ref": "base-message.json" + }, + { + "type": "object", + "required": [ + "status", + "errorMessage", + "messageType" + ], + "properties": { + "status": { + "type": "integer", + "description": "Status of the message response. Must include the numeric values for NcMethodStatus or other types which inherit from it.", + "minimum": 0, + "maximum": 65535 + }, + "errorMessage": { + "description": "Error details associated with the failure", + "type": "string" + }, + "messageType": { + "description": "Protocol message type", + "type": "integer", + "enum": [ + 5 + ] + } + } + } + ] +} diff --git a/Development/third_party/is-12/v1.0.x/APIs/schemas/event-data.json b/Development/third_party/is-12/v1.0.x/APIs/schemas/event-data.json new file mode 100644 index 000000000..9b644871c --- /dev/null +++ b/Development/third_party/is-12/v1.0.x/APIs/schemas/event-data.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Event data structure", + "title": "Event data", + "oneOf": [ + { + "$ref": "property-changed-event-data.json" + } + ] +} diff --git a/Development/third_party/is-12/v1.0.x/APIs/schemas/notification-message.json b/Development/third_party/is-12/v1.0.x/APIs/schemas/notification-message.json new file mode 100644 index 000000000..c8a64e81d --- /dev/null +++ b/Development/third_party/is-12/v1.0.x/APIs/schemas/notification-message.json @@ -0,0 +1,72 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Notification protocol message structure", + "title": "Notification protocol message", + "allOf": [ + { + "$ref": "base-message.json" + }, + { + "type": "object", + "required": [ + "notifications", + "messageType" + ], + "properties": { + "notifications": { + "description": "Notifications being transmited in this transaction", + "type": "array", + "items": { + "type": "object", + "required": [ + "oid", + "eventId", + "eventData" + ], + "properties": { + "oid": { + "type": "integer", + "description": "Emitter object id", + "minimum": 1, + "maximum": 65535 + }, + "eventId": { + "type": "object", + "description": "Event ID structure", + "required": [ + "level", + "index" + ], + "properties": { + "level": { + "type": "integer", + "description": "Level component of the event ID", + "minimum": 0, + "maximum": 65535 + }, + "index": { + "type": "integer", + "description": "Index component of the event ID", + "minimum": 1, + "maximum": 65535 + } + } + }, + "eventData": { + "$ref": "event-data.json" + } + } + } + }, + "messageType": { + "description": "Protocol message type", + "type": "integer", + "enum": [ + 2 + ] + } + } + } + ] +} \ No newline at end of file diff --git a/Development/third_party/is-12/v1.0.x/APIs/schemas/property-changed-event-data.json b/Development/third_party/is-12/v1.0.x/APIs/schemas/property-changed-event-data.json new file mode 100644 index 000000000..62249e306 --- /dev/null +++ b/Development/third_party/is-12/v1.0.x/APIs/schemas/property-changed-event-data.json @@ -0,0 +1,60 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Property changed event data structure", + "title": "Property changed event data", + "properties": { + "propertyId": { + "type": "object", + "description": "Property ID structure", + "required": [ + "level", + "index" + ], + "properties": { + "level": { + "type": "integer", + "description": "Level component of the property ID", + "minimum": 0, + "maximum": 65535 + }, + "index": { + "type": "integer", + "description": "Index component of the property ID", + "minimum": 1, + "maximum": 65535 + } + } + }, + "changeType": { + "type": "integer", + "description": "Event change type numeric value. Must include the numeric values for NcPropertyChangeType", + "minimum": 0, + "maximum": 65535 + }, + "value": { + "type": [ + "string", + "number", + "object", + "array", + "boolean", + "null" + ], + "description": "Property value as described in the MS-05-02 Class definition or in a private Class definition" + }, + "sequenceItemIndex": { + "type": [ + "number", + "null" + ], + "description": "Index of sequence item if the property is a sequence" + } + }, + "required": [ + "propertyId", + "changeType", + "value", + "sequenceItemIndex" + ] +} diff --git a/Development/third_party/is-12/v1.0.x/APIs/schemas/subscription-message.json b/Development/third_party/is-12/v1.0.x/APIs/schemas/subscription-message.json new file mode 100644 index 000000000..290ccd903 --- /dev/null +++ b/Development/third_party/is-12/v1.0.x/APIs/schemas/subscription-message.json @@ -0,0 +1,34 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Subscription protocol message structure", + "title": "Subscription protocol message", + "allOf": [ + { + "$ref": "base-message.json" + }, + { + "type": "object", + "required": [ + "subscriptions", + "messageType" + ], + "properties": { + "subscriptions": { + "description": "Array of OIDs desired for subscription", + "type": "array", + "items": { + "type": "integer" + } + }, + "messageType": { + "description": "Protocol message type", + "type": "integer", + "enum": [ + 3 + ] + } + } + } + ] +} diff --git a/Development/third_party/is-12/v1.0.x/APIs/schemas/subscription-response-message.json b/Development/third_party/is-12/v1.0.x/APIs/schemas/subscription-response-message.json new file mode 100644 index 000000000..587cdd62a --- /dev/null +++ b/Development/third_party/is-12/v1.0.x/APIs/schemas/subscription-response-message.json @@ -0,0 +1,34 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Subscription response protocol message structure", + "title": "Subscription response protocol message", + "allOf": [ + { + "$ref": "base-message.json" + }, + { + "type": "object", + "required": [ + "subscriptions", + "messageType" + ], + "properties": { + "subscriptions": { + "description": "Array of OIDs which have successfully been added to the subscription list.", + "type": "array", + "items": { + "type": "integer" + } + }, + "messageType": { + "description": "Protocol message type", + "type": "integer", + "enum": [ + 4 + ] + } + } + } + ] +} From bb6db52ca509ff9069519e4c38bd820b894c544c Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 8 Aug 2023 15:37:17 +0100 Subject: [PATCH 007/250] Add readonly on Get propertry support. Add callback to retrieve control classes from control_protocol_state --- Development/cmake/NmosCppLibraries.cmake | 6 + Development/nmos-cpp-node/main.cpp | 5 + .../nmos/control_protocol_handlers.cpp | 64 ++ Development/nmos/control_protocol_handlers.h | 44 + .../nmos/control_protocol_resource.cpp | 1018 +++++++++++++++++ Development/nmos/control_protocol_resource.h | 309 +++++ .../nmos/control_protocol_resources.cpp | 905 +-------------- Development/nmos/control_protocol_resources.h | 130 +-- Development/nmos/control_protocol_state.cpp | 23 + Development/nmos/control_protocol_state.h | 36 + Development/nmos/control_protocol_ws_api.cpp | 493 ++++---- Development/nmos/control_protocol_ws_api.h | 7 +- Development/nmos/node_server.cpp | 2 +- Development/nmos/node_server.h | 7 +- 14 files changed, 1747 insertions(+), 1302 deletions(-) create mode 100644 Development/nmos/control_protocol_handlers.cpp create mode 100644 Development/nmos/control_protocol_handlers.h create mode 100644 Development/nmos/control_protocol_resource.cpp create mode 100644 Development/nmos/control_protocol_resource.h create mode 100644 Development/nmos/control_protocol_state.cpp create mode 100644 Development/nmos/control_protocol_state.h diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index 06206a740..cce8c420b 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -831,7 +831,10 @@ set(NMOS_CPP_NMOS_SOURCES nmos/connection_api.cpp nmos/connection_events_activation.cpp nmos/connection_resources.cpp + nmos/control_protocol_handlers.cpp + nmos/control_protocol_resource.cpp nmos/control_protocol_resources.cpp + nmos/control_protocol_state.cpp nmos/control_protocol_ws_api.cpp nmos/did_sdid.cpp nmos/events_api.cpp @@ -906,7 +909,10 @@ set(NMOS_CPP_NMOS_HEADERS nmos/connection_api.h nmos/connection_events_activation.h nmos/connection_resources.h + nmos/control_protocol_handlers.h + nmos/control_protocol_resource.h nmos/control_protocol_resources.h + nmos/control_protocol_state.h nmos/control_protocol_ws_api.h nmos/device_type.h nmos/did_sdid.h diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index d2b65923f..60b0cc56b 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -10,6 +10,8 @@ #include "nmos/server.h" #include "node_implementation.h" +#include "nmos/control_protocol_state.h" + int main(int argc, char* argv[]) { // Construct our data models including mutexes to protect them @@ -107,6 +109,9 @@ int main(int argc, char* argv[]) } #endif + nmos::experimental::control_protocol_state control_protocol_state; + node_implementation.on_get_control_classes(nmos::make_get_control_protocol_classes_handler(control_protocol_state, gate)); + // Set up the node server auto node_server = nmos::experimental::make_node_server(node_model, node_implementation, log_model, gate); diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp new file mode 100644 index 000000000..3f1738c1e --- /dev/null +++ b/Development/nmos/control_protocol_handlers.cpp @@ -0,0 +1,64 @@ +#include "nmos/control_protocol_handlers.h" + +#include "cpprest/basic_utils.h" +#include "nmos/control_protocol_state.h" +#include "nmos/slog.h" + +namespace nmos +{ + get_control_protocol_classes_handler make_get_control_protocol_classes_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) + { + return [&]() + { + slog::log(gate, SLOG_FLF) << "Retrieve all control classes from cache"; + + auto lock = control_protocol_state.read_lock(); + + return control_protocol_state.control_classes; + }; + } + + get_control_protocol_class_handler make_get_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) + { + return [&](const details::nc_class_id& class_id) + { + using web::json::value; + + slog::log(gate, SLOG_FLF) << "Retrieve control class from cache"; + + auto lock = control_protocol_state.read_lock(); + + auto class_id_data = details::make_nc_class_id(class_id); + + auto& control_classes = control_protocol_state.control_classes; + auto found = control_classes.find(class_id_data); + if (control_classes.end() != found) + { + return found->second; + } + + return experimental::control_class{ value::array(), value::array(), value::array() }; + }; + } + + add_control_protocol_class_handler make_add_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) + { + return [&](const details::nc_class_id& class_id, const experimental::control_class& control_class) + { + slog::log(gate, SLOG_FLF) << "Add control class to cache"; + + auto lock = control_protocol_state.write_lock(); + + auto class_id_data = details::make_nc_class_id(class_id); + + auto& control_classes = control_protocol_state.control_classes; + if (control_classes.end() == control_classes.find(class_id_data)) + { + return false; + } + + control_classes[class_id_data] = control_class; + return true; + }; + } +} diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h new file mode 100644 index 000000000..1478235b4 --- /dev/null +++ b/Development/nmos/control_protocol_handlers.h @@ -0,0 +1,44 @@ +#ifndef NMOS_CONTROL_PROTOCOL_HANDLERS_H +#define NMOS_CONTROL_PROTOCOL_HANDLERS_H + +#include +#include +#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_state.h" + +namespace slog +{ + class base_gate; +} + +namespace nmos +{ + namespace experimental + { + struct control_class; + struct control_protocol_state; + } + + // callback to retrieve all control protocol classes + // this callback should not throw exceptions + typedef std::function get_control_protocol_classes_handler; + + // callback to retrieve a specific control protocol class + // this callback should not throw exceptions + typedef std::function get_control_protocol_class_handler; + + // callback to add user control protocol class + // this callback should not throw exceptions + typedef std::function add_control_protocol_class_handler; + + // construct callback to retrieve all control protocol classes + get_control_protocol_classes_handler make_get_control_protocol_classes_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); + + // construct callback to retrieve control protocol class + get_control_protocol_class_handler make_get_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); + + // construct callback to add control protocol class + add_control_protocol_class_handler make_add_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); +} + +#endif diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp new file mode 100644 index 000000000..68f0273f4 --- /dev/null +++ b/Development/nmos/control_protocol_resource.cpp @@ -0,0 +1,1018 @@ +#include "nmos/control_protocol_resource.h" + +//#include "nmos/resource.h" +#include "nmos/json_fields.h" + +namespace nmos +{ + namespace details + { + web::json::value make_control_protocol_result(const nc_method_result& method_result) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::status, method_result.status } + }); + } + + web::json::value make_control_protocol_error_result(const nc_method_result& method_result, const utility::string_t& error_message) + { + auto result = make_control_protocol_result(method_result); + if (!error_message.empty()) { result[nmos::fields::nc::error_message] = web::json::value::string(error_message); } + return result; + } + + web::json::value make_control_protocol_result(const nc_method_result& method_result, const web::json::value& value) + { + auto result = make_control_protocol_result(method_result); + result[nmos::fields::nc::value] = value; + return result; + } + + web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::handle, handle }, + { nmos::fields::nc::result, make_control_protocol_error_result(method_result, error_message) } + }); + } + + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::handle, handle }, + { nmos::fields::nc::result, make_control_protocol_result(method_result) } + }); + } + + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::handle, handle }, + { nmos::fields::nc::result, make_control_protocol_result(method_result, value) } + }); + } + + // message response + // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-message-type + web::json::value make_control_protocol_message_response(nc_message_type::type type, const web::json::value& responses) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::message_type, type }, + { nmos::fields::nc::responses, responses } + }); + }; + + // error message + // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#error-messages + web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::message_type, nc_message_type::error }, + { nmos::fields::nc::status, method_result.status}, + { nmos::fields::nc::error_message, error_message } + }); + }; + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid + web::json::value make_nc_class_id(const nc_class_id& class_id) + { + using web::json::value; + + auto nc_class_id = value::array(); + for (const auto class_id_item : class_id) { web::json::push_back(nc_class_id, class_id_item); } + return nc_class_id; + } + + nc_class_id parse_nc_class_id(const web::json::value& class_id_) + { + nc_class_id class_id; + for (auto& element : class_id_.as_array()) + { + class_id.push_back(element.as_integer()); + } + return class_id; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid + web::json::value make_nc_element_id(uint16_t level, uint16_t index) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::level, level }, + { nmos::fields::nc::index, index } + }); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventid + web::json::value make_nc_event_id(uint16_t level, uint16_t index) + { + return make_nc_element_id(level, index); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodid + web::json::value make_nc_method_id(uint16_t level, uint16_t index) + { + return make_nc_element_id(level, index); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertyid + web::json::value make_nc_property_id(uint16_t level, uint16_t index) + { + return make_nc_element_id(level, index); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanufacturer + web::json::value make_nc_manufacturer(const utility::string_t& name, const web::json::value& organization_id, const web::json::value& website) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::name, name }, + { nmos::fields::nc::organization_id, organization_id }, + { nmos::fields::nc::website, website } + }); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncproduct + // brand_name can be null + // uuid can be null + // description can be null + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const web::json::value& brand_name, const web::json::value& uuid, const web::json::value& description) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::name, name }, + { nmos::fields::nc::key, key }, + { nmos::fields::nc::revision_level, revision_level }, + { nmos::fields::nc::brand_name, brand_name }, + { nmos::fields::nc::uuid, uuid }, + { nmos::fields::nc::description, description } + }); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdeviceoperationalstate + // device_specific_details can be null + web::json::value make_nc_device_operational_state(nc_device_generic_state::state generic_state, const web::json::value& device_specific_details) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::generic_state, generic_state }, + { nmos::fields::nc::device_specific_details, device_specific_details } + }); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdescriptor + // description can be null + web::json::value make_nc_descriptor(const web::json::value& description) + { + using web::json::value_of; + + return value_of({ { nmos::fields::nc::description, description } }); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblockmemberdescriptor + // description can be null + // user_label can be null + web::json::value make_nc_block_member_descriptor(const web::json::value& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const web::json::value& class_id, const web::json::value& user_label, nc_oid owner) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::role] = value::string(role); + data[nmos::fields::nc::oid] = oid; + data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); + data[nmos::fields::nc::class_id] = class_id; + data[nmos::fields::nc::user_label] = user_label; + data[nmos::fields::nc::owner] = owner; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassdescriptor + // description can be null + // fixedRole can be null + web::json::value make_nc_class_descriptor(const web::json::value& description, const nc_class_id& class_id, const utility::string_t& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::fixed_role] = fixed_role; + data[nmos::fields::nc::properties] = properties; + data[nmos::fields::nc::methods] = methods; + data[nmos::fields::nc::events] = events; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncenumitemdescriptor + // description can be null + web::json::value make_nc_enum_item_descriptor(const web::json::value& description, const utility::string_t& name, uint16_t val) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::value] = val; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventdescriptor + // description can be null + // id = make_nc_event_id(level, index) + web::json::value make_nc_event_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const utility::string_t& event_datatype, bool is_deprecated) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::id] = id; + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::event_datatype] = value::string(event_datatype); + data[nmos::fields::nc::is_deprecated] = value::boolean(is_deprecated); + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncfielddescriptor + // description can be null + // type_name can be null + // constraints can be null + web::json::value make_nc_field_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::type_name] = type_name; + data[nmos::fields::nc::is_nullable] = value::boolean(is_nullable); + data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); + data[nmos::fields::nc::constraints] = constraints; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethoddescriptor + // description can be null + // id = make_nc_method_id(level, index) + // sequence parameters + web::json::value make_nc_method_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::id] = id; + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::result_datatype] = value::string(result_datatype); + data[nmos::fields::nc::parameters] = parameters; + data[nmos::fields::nc::is_deprecated] = value::boolean(is_deprecated); + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncparameterdescriptor + // description can be null + // type_name can be null + web::json::value make_nc_parameter_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::type_name] = type_name; + data[nmos::fields::nc::is_nullable] = value::boolean(is_nullable); + data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); + data[nmos::fields::nc::constraints] = constraints; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertydescriptor + // description can be null + // id = make_nc_property_id(level, index); + // type_name can be null + // constraints can be null + web::json::value make_nc_property_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const web::json::value& type_name, + bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::id] = id; + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::type_name] = type_name; + data[nmos::fields::nc::is_read_only] = value::boolean(is_read_only); + data[nmos::fields::nc::is_nullable] = value::boolean(is_nullable); + data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); + data[nmos::fields::nc::is_deprecated] = value::boolean(is_deprecated); + data[nmos::fields::nc::constraints] = constraints; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptor + // description can be null + // constraints can be null + web::json::value make_nc_datatype_descriptor(const web::json::value& description, const utility::string_t& name, nc_datatype_type::type type, const web::json::value& constraints) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::type] = type; + data[nmos::fields::nc::constraints] = constraints; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorenum + // description can be null + // constraints can be null + // items: sequence + web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const web::json::value& items) + { + auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Enum, constraints); + data[nmos::fields::nc::items] = items; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorprimitive + // description can be null + // constraints can be null + web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints) + { + return make_nc_datatype_descriptor(description, name, nc_datatype_type::Primitive, constraints); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorstruct + // description can be null + // constraints can be null + // fields: sequence + // parent_type can be null + web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const web::json::value& fields, const web::json::value& parent_type) + { + auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Struct, constraints); + data[nmos::fields::nc::fields] = fields; + data[nmos::fields::nc::parent_type] = parent_type; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptortypedef + // description can be null + // constraints can be null + web::json::value make_nc_datatype_typedef(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const utility::string_t& parent_type, bool is_sequence) + { + using web::json::value; + + auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Typedef, constraints); + data[nmos::fields::nc::parent_type] = value::string(parent_type); + data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject + web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + { + using web::json::value; + + const auto id = utility::conversions::details::to_string_t(oid); +// auto data = make_resource_core(id, user_label.is_null() ? U("") : user_label.as_string(), {}); + value data; + data[nmos::fields::id] = value::string(id); // required for nmos::resource + data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); + data[nmos::fields::nc::oid] = oid; + data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); + data[nmos::fields::nc::owner] = owner; + data[nmos::fields::nc::role] = value::string(role); + data[nmos::fields::nc::user_label] = user_label; + data[nmos::fields::nc::touchpoints] = touchpoints; + data[nmos::fields::nc::runtime_property_constraints] = runtime_property_constraints; + + return data; + }; + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members) + { + using web::json::value; + + auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); + data[nmos::fields::nc::enabled] = value::boolean(enabled); + data[nmos::fields::nc::members] = members; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager + web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + { + return make_nc_object(class_id, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, + const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, + const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause) + { + using web::json::value; + + auto data = details::make_nc_manager(nc_device_manager_class_id, oid, true, owner, U("DeviceManager"), user_label, touchpoints, runtime_property_constraints); + data[nmos::fields::nc::nc_version] = value::string(U("v1.0")); + data[nmos::fields::nc::manufacturer] = manufacturer; + data[nmos::fields::nc::product] = product; + data[nmos::fields::nc::serial_number] = value::string(serial_number); + data[nmos::fields::nc::user_inventory_code] = user_inventory_code; + data[nmos::fields::nc::device_name] = device_name; + data[nmos::fields::nc::device_role] = device_role; + data[nmos::fields::nc::operational_state] = operational_state; + data[nmos::fields::nc::reset_cause] = reset_cause; + data[nmos::fields::nc::message] = value::null(); + + return data; + } + + web::json::value make_nc_object_properties() + { + using web::json::value; + + auto properties = value::array(); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Static value. All instances of the same class will have the same identity value")), make_nc_property_id(1, 1), nmos::fields::nc::class_id, value::string(U("NcClassId")), true, false, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Object identifier")), make_nc_property_id(1, 2), nmos::fields::nc::oid, value::string(U("NcOid")), true, false, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("TRUE iff OID is hardwired into device")), make_nc_property_id(1, 3), nmos::fields::nc::constant_oid, value::string(U("NcBoolean")), true, false, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("OID of containing block. Can only ever be null for the root block")), make_nc_property_id(1, 4), nmos::fields::nc::owner, value::string(U("NcOid")), true, true, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Role of object in the containing block")), make_nc_property_id(1, 5), nmos::fields::nc::role, value::string(U("NcString")), true, false, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Scribble strip")), make_nc_property_id(1, 6), nmos::fields::nc::user_label, value::string(U("NcString")), false, true, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Touchpoints to other contexts")), make_nc_property_id(1, 7), nmos::fields::nc::touchpoints, value::string(U("NcTouchpoint")), true, true, true, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Runtime property constraints")), make_nc_property_id(1, 8), nmos::fields::nc::runtime_property_constraints, value::string(U("NcPropertyConstraints")), true, true, true, false, value::null())); + + return properties; + } + + web::json::value make_nc_object_methods() + { + using web::json::value; + + auto methods = value::array(); + { + auto parameters = value::array(); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Get property value")), make_nc_method_id(1, 1), U("Get"), U("NcMethodResultPropertyValue"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters,make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(parameters,make_nc_parameter_descriptor(value::string(U("Property value")), nmos::fields::nc::value, value::null(), true, false, value::null())); + web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Set property value")), make_nc_method_id(1, 2), U("Set"), U("NcMethodResult"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false, value::null())); + web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Get sequence item")), make_nc_method_id(1, 3), U("GetSequenceItem"), U("NcMethodResultPropertyValue"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Value")), nmos::fields::nc::value, value::null(), true, false, value::null())); + web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Set sequence item value")), make_nc_method_id(1, 4), U("SetSequenceItem"), U("NcMethodResult"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Value")), nmos::fields::nc::value, value::null(), true, false, value::null())); + web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Add item to sequence")), make_nc_method_id(1, 5), U("AddSequenceItem"), U("NcMethodResultId"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false, value::null())); + web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Delete sequence item")), make_nc_method_id(1, 6), U("RemoveSequenceItem"), U("NcMethodResult"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Get sequence length")), make_nc_method_id(1, 7), U("GetSequenceLength"), U("NcMethodResultLength"), parameters, false)); + } + + return methods; + } + + web::json::value make_nc_object_events() + { + using web::json::value; + + auto events = value::array(); + web::json::push_back(events, make_nc_event_descriptor(value::string(U("Property changed event")), make_nc_event_id(1, 1), U("PropertyChanged"), U("NcPropertyChangedEventData"), false)); + + return events; + } + + web::json::value make_nc_block_properties() + { + using web::json::value; + + auto properties = value::array(); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("TRUE if block is functional")), make_nc_property_id(2, 1), nmos::fields::nc::enabled, value::string(U("NcBoolean")), true, false, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Descriptors of this block's members")), make_nc_property_id(2, 2), nmos::fields::nc::members, value::string(U("NcBlockMemberDescriptor")), true, false, true, false, value::null())); + + return properties; + } + + web::json::value make_nc_block_methods() + { + using web::json::value; + + auto methods = value::array(); + { + auto parameters = value::array(); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("If recurse is set to true, nested members can be retrieved")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Gets descriptors of members of the block")), make_nc_method_id(2, 1), U("GetMemberDescriptors"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Relative path to search for (MUST not include the role of the block targeted by oid)")), nmos::fields::nc::path, value::string(U("NcRolePath")), false, false, value::null())); + web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Finds member(s) by path")), make_nc_method_id(2, 2), U("FindMembersByPath"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Role text to search for")), nmos::fields::nc::role, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Signals if the comparison should be case sensitive")), nmos::fields::nc::case_sensitive, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("TRUE to only return exact matches")), nmos::fields::nc::match_whole_string, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("TRUE to search nested blocks")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Finds members with given role name or fragment")), make_nc_method_id(2, 3), U("FindMembersByRole"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Class id to search for")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("If TRUE it will also include derived class descriptors")), nmos::fields::nc::include_derived, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("TRUE to search nested blocks")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Finds members with given class id")), details::make_nc_method_id(2, 4), U("FindMembersByClassId"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + } + + return methods; + } + + web::json::value make_nc_block_events() + { + using web::json::value; + + return value::array(); + } + + web::json::value make_nc_worker_properties() + { + using web::json::value; + + auto properties = value::array(); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("TRUE iff worker is enabled")), make_nc_property_id(2, 1), nmos::fields::nc::enabled, value::string(U("NcBoolean")), false, false, false, false, value::null())); + + return properties; + } + + web::json::value make_nc_worker_methods() + { + using web::json::value; + + return value::array(); + } + + web::json::value make_nc_worker_events() + { + using web::json::value; + + return value::array(); + } + + web::json::value make_nc_manager_properties() + { + using web::json::value; + + return value::array(); + } + + web::json::value make_nc_manager_methods() + { + using web::json::value; + + return value::array(); + } + + web::json::value make_nc_manager_events() + { + using web::json::value; + + return value::array(); + } + + web::json::value make_nc_device_manager_properties() + { + using web::json::value; + + auto properties = value::array(); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Version of MS-05-02 that this device uses")), make_nc_property_id(3, 1), nmos::fields::nc::nc_version, value::string(U("NcVersionCode")), true, false, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Manufacturer descriptor")), make_nc_property_id(3, 2), nmos::fields::nc::manufacturer, value::string(U("NcManufacturer")), true, false, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Product descriptor")), make_nc_property_id(3, 3), nmos::fields::nc::product, value::string(U("NcProduct")), true, false, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Serial number")), make_nc_property_id(3, 4), nmos::fields::nc::serial_number, value::string(U("NcString")), true, false, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Asset tracking identifier (user specified)")), make_nc_property_id(3, 5), nmos::fields::nc::user_inventory_code, value::string(U("NcString")), false, true, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Name of this device in the application. Instance name, not product name")), make_nc_property_id(3, 6), nmos::fields::nc::device_name, value::string(U("NcString")), false, true, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Role of this device in the application")), make_nc_property_id(3, 7), nmos::fields::nc::device_role, value::string(U("NcString")), false, true, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Device operational state")), make_nc_property_id(3, 8), nmos::fields::nc::operational_state, value::string(U("NcDeviceOperationalState")), true, false, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Reason for most recent reset")), make_nc_property_id(3, 9), nmos::fields::nc::reset_cause, value::string(U("NcResetCause")), true, false, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Arbitrary message from dev to controller")), make_nc_property_id(3, 10), nmos::fields::nc::message, value::string(U("NcString")), true, true, false, false, value::null())); + + return properties; + } + + web::json::value make_nc_device_manager_methods() + { + using web::json::value; + + return value::array(); + } + + web::json::value make_nc_device_manager_events() + { + using web::json::value; + + return value::array(); + } + + web::json::value make_nc_class_manager_properties() + { + using web::json::value; + + auto properties = value::array(); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Descriptions of all control classes in the device (descriptors do not contain inherited elements)")), make_nc_property_id(3, 1), nmos::fields::nc::control_classes, value::string(U("NcClassDescriptor")), true, false, true, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Descriptions of all data types in the device (descriptors do not contain inherited elements)")), make_nc_property_id(3, 2), nmos::fields::nc::datatypes, value::string(U("NcDatatypeDescriptor")), true, false, true, false, value::null())); + + return properties; + } + + web::json::value make_nc_class_manager_methods() + { + using web::json::value; + + auto methods = value::array(); + { + auto parameters = value::array(); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("class ID")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("If set the descriptor would contain all inherited elements")), nmos::fields::nc::include_inherited, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Get a single class descriptor")), make_nc_method_id(3, 1), U("GetControlClass"), U("NcMethodResultClassDescriptor"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("name of datatype")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("If set the descriptor would contain all inherited elements")), nmos::fields::nc::include_inherited, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Get a single datatype descriptor")), make_nc_method_id(3, 2), U("GetDatatype"), U("NcMethodResultDatatypeDescriptor"), parameters, false)); + } + + return methods; + } + + web::json::value make_nc_class_manager_events() + { + using web::json::value; + + return value::array(); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label) + { + using web::json::value; + + auto data = make_nc_manager(nc_class_manager_class_id, oid, true, owner, U("ClassManager"), user_label); + + // load the minimal control classes + data[nmos::fields::nc::control_classes] = value::array(); + auto& control_classes = data[nmos::fields::nc::control_classes]; + + // NcObject control class + web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcObject class descriptor")), nc_object_class_id, U("NcObject"), value::null(), make_nc_object_properties(), make_nc_object_methods(), make_nc_object_events())); + // NcBlock control class + web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcBlock class descriptor")), nc_block_class_id, U("NcBlock"), value::null(), make_nc_block_properties(), make_nc_block_methods(), make_nc_block_events())); + // NcWorker control class + web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcWorker class descriptor")), nc_worker_class_id, U("NcWorker"), value::null(), make_nc_worker_properties(), make_nc_worker_methods(), make_nc_worker_events())); + // NcManager control class + web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcManager class descriptor")), nc_manager_class_id, U("NcManager"), value::null(), make_nc_manager_properties(), make_nc_manager_methods(), make_nc_manager_events())); + // NcDeviceManager control class + web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcDeviceManager class descriptor")), nc_device_manager_class_id, U("NcDeviceManager"), value::string(U("DeviceManager")), make_nc_device_manager_properties(), make_nc_device_manager_methods(), make_nc_device_manager_events())); + // NcClassManager control class + web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcClassManager class descriptor")), nc_class_manager_class_id, U("NcClassManager"), value::string(U("ClassManager")), make_nc_class_manager_properties(), make_nc_class_manager_methods(), make_nc_class_manager_events())); + + // load the minimal datatypes + data[nmos::fields::nc::datatypes] = value::array(); + auto& datatypes = data[nmos::fields::nc::datatypes]; + + // NcObject datatypes + // NcClassId + web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Sequence of class ID fields")), U("NcClassId"), value::null(), U("NcInt32"), true)); + // NcOid + web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Object id")), U("NcOid"), value::null(), U("NcUint32"), false)); + // NcTouchpoint + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Context namespace")), nmos::fields::nc::context_namespace, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Base touchpoint class")), U("NcTouchpoint"), value::null(), fields, value::null())); + } + // NcElementId + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Level of the element")), nmos::fields::nc::level, value::string(U("NcUint16")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Index of the element")), nmos::fields::nc::index, value::string(U("NcUint16")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Class element id which contains the level and index")), U("NcElementId"), value::null(), fields, value::null())); + } + // NcPropertyId + { + auto fields = value::array(); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Property id which contains the level and index")), U("NcPropertyId"), value::null(), fields, value::string(U("NcElementId")))); + } + // NcPropertyConstraints + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("The id of the property being constrained")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional default value")), nmos::fields::nc::default_value, value::null(), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Property constraints class")), U("NcPropertyConstraints"), value::null(), fields, value::null())); + } + // NcMethodResultPropertyValue + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Getter method value for the associated property")), nmos::fields::nc::value, value::null(), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Result when invoking the getter method associated with a property")), U("NcMethodResultPropertyValue"), value::null(), fields, value::string(U("NcMethodResult")))); + } + // NcMethodStatus + { + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful")), U("Ok"), 200)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful but targeted property is deprecated")), U("PropertyDeprecated"), 298)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful but method is deprecated")), U("MethodDeprecated"), 299)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Badly-formed command (e.g. the incoming command has invalid message encoding and cannot be parsed by the underlying protocol)")), U("BadCommandFormat"), 400)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Client is not authorized")), U("Unauthorized"), 401)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Command addresses a nonexistent object")), U("BadOid"), 404)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Attempt to change read-only state")), U("Readonly"), 405)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call is invalid in current operating context (e.g. attempting to invoke a method when the object is disabled)")), U("InvalidRequest"), 406)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("There is a conflict with the current state of the device")), U("Conflict"), 409)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Something was too big")), U("BufferOverflow"), 413)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Index is outside the available range")), U("IndexOutOfBounds"), 414)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method parameter does not meet expectations (e.g. attempting to invoke a method with an invalid type for one of its parameters)")), U("ParameterError"), 417)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed object is locked")), U("Locked"), 423)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Internal device error")), U("DeviceError"), 500)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed method is not implemented by the addressed object")), U("MethodNotImplemented"), 501)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed property is not implemented by the addressed object")), U("PropertyNotImplemented"), 502)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("The device is not ready to handle any commands")), U("NotReady"), 503)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call did not finish within the allotted time")), U("Timeout"), 504)); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Method invokation status")), U("NcMethodStatus"), value::null(), items)); + } + // NcMethodResult + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Status for the invoked method")), nmos::fields::nc::status, value::string(U("NcMethodStatus")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Base result of the invoked method")), U("NcMethodResult"), value::null(), fields, value::null())); + } + // NcId + web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Identity handler")), U("NcId"), value::null(), U("NcUint32"), false)); + // NcMethodResultId + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Id result value")), nmos::fields::nc::value, value::string(U("NcId")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Id method result")), U("NcMethodResultId"), value::null(), fields, value::string(U("NcMethodResult")))); + } + // NcMethodResultLength + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Length result value")), nmos::fields::nc::value, value::string(U("NcUint32")), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Length method result")), U("NcMethodResultLength"), value::null(), fields, value::string(U("NcMethodResult")))); + } + // NcPropertyChangeType + { + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Current value changed")), U("ValueChanged"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item added")), U("SequenceItemAdded"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item changed")), U("SequenceItemChanged"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item removed")), U("SequenceItemRemoved"), 3)); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Type of property change")), U("NcPropertyChangeType"), value::null(), items)); + } + // NcPropertyChangedEventData + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("The id of the property that changed")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Information regarding the change type")), nmos::fields::nc::change_type, value::string(U("NcPropertyChangeType")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property-type specific value")), nmos::fields::nc::value, value::null(), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Index of sequence item if the property is a sequence")), nmos::fields::nc::sequence_item_index, value::string(U("NcId")), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Payload of property-changed event")), U("NcPropertyChangedEventData"), value::null(), fields, value::null())); + } + + // NcBlock datatypes + // NcDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional user facing description")), nmos::fields::nc::description, value::string(U("NcString")), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Base descriptor")), U("NcDescriptor"), value::null(), fields, value::null())); + } + // NcBlockMemberDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Role of member in its containing block")), nmos::fields::nc::role, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("OID of member")), nmos::fields::nc::oid, value::string(U("NcOid")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff member's OID is hardwired into device")), nmos::fields::nc::constant_oid, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Class ID")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("User label")), nmos::fields::nc::user_label, value::string(U("NcString")), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Containing block's OID")), nmos::fields::nc::owner, value::string(U("NcOid")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor which is specific to a block member")), U("NcBlockMemberDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); + } + // NcMethodResultBlockMemberDescriptors + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Block member descriptors method result value")), nmos::fields::nc::value, value::string(U("NcBlockMemberDescriptor")), false, true, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing block member descriptors as the value")), U("NcMethodResultBlockMemberDescriptors"), value::null(), fields, value::string(U("NcMethodResult")))); + } + + // NcWorker has no datatypes + + // NcManager has no datatypes + + // NcDeviceManager datatypes + // NcVersionCode + web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Version code in semantic versioning format")), U("NcVersionCode"), value::null(), U("NcString"), false)); + // NcOrganizationId + web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Unique 24-bit organization id")), U("NcOrganizationId"), value::null(), U("NcInt32"), false)); + // NcUri + web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Uniform resource identifier")), U("NcUri"), value::null(), U("NcString"), false)); + // NcManufacturer + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's name")), nmos::fields::nc::name, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("IEEE OUI or CID of manufacturer")), nmos::fields::nc::organization_id, value::string(U("NcOrganizationId")), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("URL of the manufacturer's website")), nmos::fields::nc::website, value::string(U("NcUri")), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Manufacturer descriptor")), U("NcManufacturer"), value::null(), fields, value::null())); + } + // NcUuid + web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("UUID")), U("NcUuid"), value::null(), U("NcString"), false)); + // NcProduct + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Product name")), nmos::fields::nc::name, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's unique key to product - model number, SKU, etc")), nmos::fields::nc::key, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's product revision level code")), nmos::fields::nc::revision_level, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Brand name under which product is sold")), nmos::fields::nc::brand_name, value::string(U("NcString")), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Unique UUID of product (not product instance)")), nmos::fields::nc::uuid, value::string(U("NcUuid")), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Text description of product")), nmos::fields::nc::description, value::string(U("NcString")), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Product descriptor")), U("NcProduct"), value::null(), fields, value::null())); + } + // NcDeviceGenericState + { + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Normal operation")), U("NormalOperation"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is initializing")), U("Initializing"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is performing a software or firmware update")), U("Updating"), 3)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is experiencing a licensing error")), U("LicensingError"), 4)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is experiencing an internal error")), U("InternalError"), 5)); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Device generic operational state")), U("NcDeviceGenericState"), value::null(), items)); + } + // NcDeviceOperationalState + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Generic operational state")), nmos::fields::nc::generic_state, value::string(U("NcDeviceGenericState")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Specific device details")), nmos::fields::nc::device_specific_details, value::string(U("NcString")), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Device operational state")), U("NcDeviceOperationalState"), value::null(), fields, value::null())); + } + // NcResetCause + { + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Power on")), U("PowerOn"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Internal error")), U("InternalError"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Upgrade")), U("Upgrade"), 3)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Controller request")), U("ControllerRequest"), 4)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Manual request from the front panel")), U("ManualReset"), 5)); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Reset cause enum")), U("NcResetCause"), value::null(), items)); + } + + // NcClassManager datatypes + // NcName + web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Programmatically significant name, alphanumerics + underscore, no spaces")), U("NcName"), value::null(), U("NcString"), false)); + // NcPropertyDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property id with level and index")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of property")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of property's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is read-only")), nmos::fields::nc::is_read_only, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class property")), U("NcPropertyDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); + } + // NcMethodId + { + auto fields = value::array(); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Method id which contains the level and index")), U("NcMethodId"), value::null(), fields, value::string(U("NcElementId")))); + } + // NcParameterDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of parameter")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of parameter's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a method parameter")), U("NcParameterDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); + } + // NcMethodDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Method id with level and index")), nmos::fields::nc::id, value::string(U("NcMethodId")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of method")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of method result's datatype")), nmos::fields::nc::result_datatype, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Parameter descriptors if any")), nmos::fields::nc::parameters, value::string(U("NcParameterDescriptor")), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class method")), U("NcMethodDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); + } + // NcEventId + { + auto fields = value::array(); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Event id which contains the level and index")), U("NcEventId"), value::null(), fields, value::string(U("NcElementId")))); + } + // NcEventDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Event id with level and index")), nmos::fields::nc::id, value::string(U("NcEventId")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of event")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of event data's datatype")), nmos::fields::nc::event_datatype, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class event")), U("NcEventDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); + } + // NcClassDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Identity of the class")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of the class")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Role if the class has fixed role (manager classes)")), nmos::fields::nc::fixed_role, value::string(U("NcString")), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property descriptors")), nmos::fields::nc::properties, value::string(U("NcPropertyDescriptor")), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Method descriptors")), nmos::fields::nc::methods, value::string(U("NcMethodDescriptor")), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Event descriptors")), nmos::fields::nc::events, value::string(U("NcEventDescriptor")), false, true, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class")), U("NcClassDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); + } + // NcParameterConstraints + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Default value")), nmos::fields::nc::default_value, value::null(), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Abstract parameter constraints class")), U("NcParameterConstraints"), value::null(), fields, value::null())); + } + // NcDatatypeType + { + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Primitive datatype")), U("Primitive"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Simple alias of another datatype")), U("Typedef"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Data structure")), U("Struct"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Enum datatype")), U("Enum"), 3)); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Datatype type")), U("NcDatatypeType"), value::null(), items)); + } + // NcDatatypeDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Datatype name")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Type: Primitive, Typedef, Struct, Enum")), nmos::fields::nc::type, value::string(U("NcDatatypeType")), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Base datatype descriptor")), U("NcDatatypeDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); + } + // NcMethodResultClassDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Class descriptor method result value")), nmos::fields::nc::value, value::string(U("NcClassDescriptor")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing a class descriptor as the value")), U("NcMethodResultClassDescriptor"), value::null(), fields, value::string(U("NcMethodResult")))); + } + // NcMethodResultDatatypeDescriptor + { + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Datatype descriptor method result value")), nmos::fields::nc::value, value::string(U("NcDatatypeDescriptor")), false, false, value::null())); + web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing a datatype descriptor as the value")), U("NcMethodResultDatatypeDescriptor"), value::null(), fields, value::string(U("NcMethodResult")))); + } + + return data; + } + } +} diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h new file mode 100644 index 000000000..bfecdd8e0 --- /dev/null +++ b/Development/nmos/control_protocol_resource.h @@ -0,0 +1,309 @@ +#ifndef NMOS_CONTROL_PROTOCOL_RESOURCE_H +#define NMOS_CONTROL_PROTOCOL_RESOURCE_H + +#include +#include "cpprest/json_utils.h" + +namespace web +{ + namespace json + { + class value; + } +} + +namespace nmos +{ + namespace details + { + namespace nc_message_type + { + enum type + { + command = 0, + command_response = 1, + notification = 2, + subscription = 3, + subscription_response = 4, + error = 5 + }; + } + + // Method invokation status + namespace nc_method_status + { + enum status + { + ok = 200, // Method call was successful + property_deprecated = 298, // Method call was successful but targeted property is deprecated + method_deprecated = 299, // Method call was successful but method is deprecated + bad_command_format = 400, // Badly-formed command + unathorized = 401, // Client is not authorized + bad_oid = 404, // Command addresses a nonexistent object + read_only = 405, // Attempt to change read-only state + invalid_request = 406, // Method call is invalid in current operating context + conflict = 409, // There is a conflict with the current state of the device + buffer_overflow = 413, // Something was too big + parameter_error = 417, // Method parameter does not meet expectations + locked = 423, // Addressed object is locked + device_error = 500, // Internal device error + method_not_implemented = 501, // Addressed method is not implemented by the addressed object + property_not_implemented = 502, // Addressed property is not implemented by the addressed object + not_ready = 503, // The device is not ready to handle any commands + timeout = 504, // Method call did not finish within the allotted time + property_version_error = 505 // Incompatible protocol version + }; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodresult + struct nc_method_result + { + nc_method_status::status status; + }; + + // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-response-message-type + web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message); + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result); + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value); + + // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-message-type + web::json::value make_control_protocol_message_response(nc_message_type::type type, const web::json::value& responses); + // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#error-messages + web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message); + + // Datatype type + namespace nc_datatype_type + { + enum type + { + Primitive = 0, + Typedef = 1, + Struct = 2, + Enum = 3 + }; + } + + // Device generic operational state + namespace nc_device_generic_state + { + enum state + { + Unknown = 0, // Unknown + NormalOperation = 1, // Normal operation + Initializing = 2, // Device is initializing + Updating = 3, // Device is performing a software or firmware update + LicensingError = 4, // Device is experiencing a licensing error + InternalError = 5 // Device is experiencing an internal error + }; + } + + // Reset cause enum + namespace nc_reset_cause + { + enum cause + { + Unknown = 0, // 0 Unknown + Power_on = 1, // 1 Power on + InternalError = 2, // 2 Internal error + Upgrade = 3, // 3 Upgrade + Controller_request = 4, // 4 Controller request + ManualReset = 5 // 5 Manual request from the front panel + }; + } + + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncid + typedef uint32_t nc_id; + + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncoid + typedef uint32_t nc_oid; + + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncuri + typedef utility::string_t nc_uri; + + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncuuid + typedef utility::string_t nc_uuid; + + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid + typedef std::vector nc_class_id; + const nc_class_id nc_object_class_id({ 1 }); + const nc_class_id nc_block_class_id({ 1, 1 }); + const nc_class_id nc_worker_class_id({ 1, 2 }); + const nc_class_id nc_manager_class_id({ 1, 3 }); + const nc_class_id nc_device_manager_class_id({ 1, 3, 1 }); + const nc_class_id nc_class_manager_class_id({ 1, 3, 2 }); + + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nctouchpoint + typedef utility::string_t nc_touch_point; + + typedef std::map properties; + +// typedef std::function method; +// typedef std::map methods; // method_id vs method handler + + typedef std::function method; + typedef std::map methods; // method_id vs method handler + + web::json::value make_control_protocol_result(const nc_method_result& method_result); + web::json::value make_control_protocol_error_result(const nc_method_result& method_result, const utility::string_t& error_message); + + web::json::value make_control_protocol_result(const nc_method_result& method_result, const web::json::value& value); + web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message); + + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result); + + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value); + + // message response + // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-message-type + web::json::value make_control_protocol_message_response(nc_message_type::type type, const web::json::value& responses); + + // error message + // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#error-messages + web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid + web::json::value make_nc_class_id(const nc_class_id& class_id); + nc_class_id parse_nc_class_id(const web::json::value& class_id); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid + web::json::value make_nc_element_id(uint16_t level, uint16_t index); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventid + web::json::value make_nc_event_id(uint16_t level, uint16_t index); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodid + web::json::value make_nc_method_id(uint16_t level, uint16_t index); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertyid + web::json::value make_nc_property_id(uint16_t level, uint16_t index); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanufacturer + web::json::value make_nc_manufacturer(const utility::string_t& name, const web::json::value& organization_id = web::json::value::null(), const web::json::value& website = web::json::value::null()); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncproduct + // brand_name can be null + // uuid can be null + // description can be null + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const web::json::value& brand_name = web::json::value::null(), const web::json::value& uuid = web::json::value::null(), const web::json::value& description = web::json::value::null()); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdeviceoperationalstate + // device_specific_details can be null + web::json::value make_nc_device_operational_state(nc_device_generic_state::state generic_state, const web::json::value& device_specific_details); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdescriptor + // description can be null + web::json::value make_nc_descriptor(const web::json::value& description); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblockmemberdescriptor + // description can be null + // user_label can be null + web::json::value make_nc_block_member_descriptor(const web::json::value& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const web::json::value& class_id, const web::json::value& user_label, nc_oid owner); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassdescriptor + // description can be null + // fixedRole can be null + web::json::value make_nc_class_descriptor(const web::json::value& description, const web::json::value& class_id, const utility::string_t& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncenumitemdescriptor + // description can be null + web::json::value make_nc_enum_item_descriptor(const web::json::value& description, const utility::string_t& name, uint16_t val); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventdescriptor + // description can be null + // id = make_nc_event_id(level, index) + web::json::value make_nc_event_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const utility::string_t& event_datatype, bool is_deprecated); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncfielddescriptor + // description can be null + // type_name can be null + // constraints can be null + web::json::value make_nc_field_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethoddescriptor + // description can be null + // id = make_nc_method_id(level, index) + // sequence parameters + web::json::value make_nc_method_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncparameterdescriptor + // description can be null + // type_name can be null + web::json::value make_nc_parameter_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertydescriptor + // description can be null + // id = make_nc_property_id(level, index); + // type_name can be null + // constraints can be null + web::json::value make_nc_property_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const web::json::value& type_name, + bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptor + // description can be null + // constraints can be null + web::json::value make_nc_datatype_descriptor(const web::json::value& description, const utility::string_t& name, nc_datatype_type::type type, const web::json::value& constraints); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorenum + // description can be null + // constraints can be null + // items: sequence + web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const web::json::value& items); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorprimitive + // description can be null + // constraints can be null + web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorstruct + // description can be null + // constraints can be null + // fields: sequence + // parent_type can be null + web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const web::json::value& fields, const web::json::value& parent_type); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptortypedef + // description can be null + // constraints can be null + web::json::value make_nc_datatype_typedef(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const utility::string_t& parent_type, bool is_sequence); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject + web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager + web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null()); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, + const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, + const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + web::json::value make_nc_class_manager(details::nc_oid oid, nc_oid owner, const web::json::value& user_label); + + // make the core classes proprties/methods/events + web::json::value make_nc_object_properties(); + web::json::value make_nc_object_methods(); + web::json::value make_nc_object_events(); + web::json::value make_nc_block_properties(); + web::json::value make_nc_block_methods(); + web::json::value make_nc_block_events(); + web::json::value make_nc_worker_properties(); + web::json::value make_nc_worker_methods(); + web::json::value make_nc_worker_events(); + web::json::value make_nc_manager_properties(); + web::json::value make_nc_manager_methods(); + web::json::value make_nc_manager_events(); + web::json::value make_nc_device_manager_properties(); + web::json::value make_nc_device_manager_methods(); + web::json::value make_nc_device_manager_events(); + web::json::value make_nc_class_manager_properties(); + web::json::value make_nc_class_manager_methods(); + web::json::value make_nc_class_manager_events(); + } +} + +#endif diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index 1fa4316b8..f5e2754b4 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -1,908 +1,11 @@ #include "nmos/control_protocol_resources.h" +#include "nmos/control_protocol_resource.h" #include "nmos/resource.h" #include "nmos/is12_versions.h" namespace nmos { - namespace details - { - web::json::value make_control_protocol_result(const nc_method_result& method_result) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::status, method_result.status } - }); - } - - web::json::value make_control_protocol_error_result(const nc_method_result& method_result, const utility::string_t& error_message) - { - auto result = make_control_protocol_result(method_result); - if (!error_message.empty()) { result[nmos::fields::nc::error_message] = web::json::value::string(error_message); } - return result; - } - - web::json::value make_control_protocol_result(const nc_method_result& method_result, const web::json::value& value) - { - auto result = make_control_protocol_result(method_result); - result[nmos::fields::nc::value] = value; - return result; - } - - web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::handle, handle }, - { nmos::fields::nc::result, make_control_protocol_error_result(method_result, error_message) } - }, true); - } - - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::handle, handle }, - { nmos::fields::nc::result, make_control_protocol_result(method_result) } - }, true); - } - - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::handle, handle }, - { nmos::fields::nc::result, make_control_protocol_result(method_result, value) } - }, true); - } - - // message response - // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-message-type - web::json::value make_control_protocol_message_response(nc_message_type::type type, const web::json::value& responses) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::message_type, type }, - { nmos::fields::nc::responses, responses } - }, true); - }; - - // error message - // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#error-messages - web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::message_type, nc_message_type::error }, - { nmos::fields::nc::status, method_result.status}, - { nmos::fields::nc::error_message, error_message } - }, true); - }; - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid - web::json::value make_nc_class_id(const nc_class_id& class_id) - { - using web::json::value; - - auto nc_class_id = value::array(); - for (const auto class_id_item : class_id) { web::json::push_back(nc_class_id, class_id_item); } - return nc_class_id; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid - web::json::value make_nc_element_id(uint16_t level, uint16_t index) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::level, level }, - { nmos::fields::nc::index, index } - }); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventid - web::json::value make_nc_event_id(uint16_t level, uint16_t index) - { - return make_nc_element_id(level, index); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodid - web::json::value make_nc_method_id(uint16_t level, uint16_t index) - { - return make_nc_element_id(level, index); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertyid - web::json::value make_nc_property_id(uint16_t level, uint16_t index) - { - return make_nc_element_id(level, index); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanufacturer - web::json::value make_nc_manufacturer(const utility::string_t& name, const web::json::value& organization_id = web::json::value::null(), const web::json::value& website = web::json::value::null()) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::name, name }, - { nmos::fields::nc::organization_id, organization_id }, - { nmos::fields::nc::website, website } - }, true); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncproduct - // brand_name can be null - // uuid can be null - // description can be null - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, - const web::json::value& brand_name = web::json::value::null(), const web::json::value& uuid = web::json::value::null(), const web::json::value& description = web::json::value::null()) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::name, name }, - { nmos::fields::nc::key, key }, - { nmos::fields::nc::revision_level, revision_level }, - { nmos::fields::nc::brand_name, brand_name }, - { nmos::fields::nc::uuid, uuid }, - { nmos::fields::nc::description, description } - }, true); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdeviceoperationalstate - // device_specific_details can be null - web::json::value make_nc_device_operational_state(nc_device_generic_state::state generic_state, const web::json::value& device_specific_details) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::generic_state, generic_state }, - { nmos::fields::nc::device_specific_details, device_specific_details } - }, true); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdescriptor - // description can be null - web::json::value make_nc_descriptor(const web::json::value& description) - { - using web::json::value_of; - - return value_of({ { nmos::fields::nc::description, description } }); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblockmemberdescriptor - // description can be null - // user_label can be null - web::json::value make_nc_block_member_descriptor(const web::json::value& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const web::json::value& class_id, const web::json::value& user_label, nc_oid owner) - { - using web::json::value; - - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::role] = value::string(role); - data[nmos::fields::nc::oid] = oid; - data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); - data[nmos::fields::nc::class_id] = class_id; - data[nmos::fields::nc::user_label] = user_label; - data[nmos::fields::nc::owner] = owner; - - return data; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassdescriptor - // description can be null - // fixedRole can be null - web::json::value make_nc_class_descriptor(const web::json::value& description, const web::json::value& class_id, const utility::string_t& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) - { - using web::json::value; - - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::class_id] = class_id; - data[nmos::fields::nc::name] = value::string(name); - data[nmos::fields::nc::fixed_role] = fixed_role; - data[nmos::fields::nc::properties] = properties; - data[nmos::fields::nc::methods] = methods; - data[nmos::fields::nc::events] = events; - - return data; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncenumitemdescriptor - // description can be null - web::json::value make_nc_enum_item_descriptor(const web::json::value& description, const utility::string_t& name, uint16_t val) - { - using web::json::value; - - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::name] = value::string(name); - data[nmos::fields::nc::value] = val; - - return data; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventdescriptor - // description can be null - // id = make_nc_event_id(level, index) - web::json::value make_nc_event_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const utility::string_t& event_datatype, bool is_deprecated) - { - using web::json::value; - - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::id] = id; - data[nmos::fields::nc::name] = value::string(name); - data[nmos::fields::nc::event_datatype] = value::string(event_datatype); - data[nmos::fields::nc::is_deprecated] = value::boolean(is_deprecated); - - return data; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncfielddescriptor - // description can be null - // type_name can be null - // constraints can be null - web::json::value make_nc_field_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) - { - using web::json::value; - - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::name] = value::string(name); - data[nmos::fields::nc::type_name] = type_name; - data[nmos::fields::nc::is_nullable] = value::boolean(is_nullable); - data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); - data[nmos::fields::nc::constraints] = constraints; - - return data; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethoddescriptor - // description can be null - // id = make_nc_method_id(level, index) - // sequence parameters - web::json::value make_nc_method_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) - { - using web::json::value; - - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::id] = id; - data[nmos::fields::nc::name] = value::string(name); - data[nmos::fields::nc::result_datatype] = value::string(result_datatype); - data[nmos::fields::nc::parameters] = parameters; - data[nmos::fields::nc::is_deprecated] = value::boolean(is_deprecated); - - return data; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncparameterdescriptor - // description can be null - // type_name can be null - web::json::value make_nc_parameter_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) - { - using web::json::value; - - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::name] = value::string(name); - data[nmos::fields::nc::type_name] = type_name; - data[nmos::fields::nc::is_nullable] = value::boolean(is_nullable); - data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); - data[nmos::fields::nc::constraints] = constraints; - - return data; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertydescriptor - // description can be null - // id = make_nc_property_id(level, index); - // type_name can be null - // constraints can be null - web::json::value make_nc_property_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const web::json::value& type_name, - bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) - { - using web::json::value; - - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::id] = id; - data[nmos::fields::nc::name] = value::string(name); - data[nmos::fields::nc::type_name] = type_name; - data[nmos::fields::nc::is_read_only] = value::boolean(is_read_only); - data[nmos::fields::nc::is_nullable] = value::boolean(is_nullable); - data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); - data[nmos::fields::nc::is_deprecated] = value::boolean(is_deprecated); - data[nmos::fields::nc::constraints] = constraints; - - return data; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptor - // description can be null - // constraints can be null - web::json::value make_nc_datatype_descriptor(const web::json::value& description, const utility::string_t& name, nc_datatype_type::type type, const web::json::value& constraints) - { - using web::json::value; - - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::name] = value::string(name); - data[nmos::fields::nc::type] = type; - data[nmos::fields::nc::constraints] = constraints; - - return data; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorenum - // description can be null - // constraints can be null - // items: sequence - web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const web::json::value& items) - { - auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Enum, constraints); - data[nmos::fields::nc::items] = items; - - return data; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorprimitive - // description can be null - // constraints can be null - web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints) - { - return make_nc_datatype_descriptor(description, name, nc_datatype_type::Primitive, constraints); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorstruct - // description can be null - // constraints can be null - // fields: sequence - // parent_type can be null - web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const web::json::value& fields, const web::json::value& parent_type) - { - auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Struct, constraints); - data[nmos::fields::nc::fields] = fields; - data[nmos::fields::nc::parent_type] = parent_type; - - return data; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptortypedef - // description can be null - // constraints can be null - web::json::value make_nc_datatype_typedef(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const utility::string_t& parent_type, bool is_sequence) - { - using web::json::value; - - auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Typedef, constraints); - data[nmos::fields::nc::parent_type] = value::string(parent_type); - data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); - - return data; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject - web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) - { - using web::json::value; - - const auto id = utility::conversions::details::to_string_t(oid); - auto data = make_resource_core(id, user_label.is_null() ? U("") : user_label.as_string(), {}); - data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); - data[nmos::fields::nc::oid] = oid; - data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); - data[nmos::fields::nc::owner] = owner; - data[nmos::fields::nc::role] = value::string(role); - data[nmos::fields::nc::user_label] = user_label; - data[nmos::fields::nc::touchpoints] = touchpoints; - data[nmos::fields::nc::runtime_property_constraints] = runtime_property_constraints; - - return data; - }; - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock - web::json::value make_nc_block(nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members) - { - using web::json::value; - - auto data = details::make_nc_object({ 1, 1 }, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); - data[nmos::fields::nc::enabled] = value::boolean(enabled); - data[nmos::fields::nc::members] = members; - - return data; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager - web::json::value make_nc_manager(nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null()) - { - return make_nc_object({ 1, 3 }, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager - web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, - const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, - const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause) - { - using web::json::value; - - auto data = details::make_nc_manager(oid, true, owner, U("DeviceManager"), user_label, touchpoints, runtime_property_constraints); - data[nmos::fields::nc::class_id] = details::make_nc_class_id({ 1, 3, 1 }); - data[nmos::fields::nc::nc_version] = value::string(U("v1.0")); - data[nmos::fields::nc::manufacturer] = manufacturer; - data[nmos::fields::nc::product] = product; - data[nmos::fields::nc::serial_number] = value::string(serial_number); - data[nmos::fields::nc::user_inventory_code] = user_inventory_code; - data[nmos::fields::nc::device_name] = device_name; - data[nmos::fields::nc::device_role] = device_role; - data[nmos::fields::nc::operational_state] = operational_state; - data[nmos::fields::nc::reset_cause] = reset_cause; - data[nmos::fields::nc::message] = value::null(); - - return data; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager(details::nc_oid oid, nc_oid owner, const web::json::value& user_label) - { - using web::json::value; - - auto data = details::make_nc_manager(oid, true, owner, U("ClassManager"), user_label); - data[nmos::fields::nc::class_id] = details::make_nc_class_id({ 1, 3, 2 }); - - // load the minimal control classes - data[nmos::fields::nc::control_classes] = value::array(); - auto& control_classes = data[nmos::fields::nc::control_classes]; - - // NcObject control class - { - auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Static value. All instances of the same class will have the same identity value")), details::make_nc_property_id(1, 1), nmos::fields::nc::class_id, value::string(U("NcClassId")), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Object identifier")), details::make_nc_property_id(1, 2), nmos::fields::nc::oid, value::string(U("NcOid")), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("TRUE iff OID is hardwired into device")), details::make_nc_property_id(1, 3), nmos::fields::nc::constant_oid, value::string(U("NcBoolean")), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("OID of containing block. Can only ever be null for the root block")), details::make_nc_property_id(1, 4), nmos::fields::nc::owner, value::string(U("NcOid")), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Role of object in the containing block")), details::make_nc_property_id(1, 5), nmos::fields::nc::role, value::string(U("NcString")), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Scribble strip")), details::make_nc_property_id(1, 6), nmos::fields::nc::user_label, value::string(U("NcString")), false, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Touchpoints to other contexts")), details::make_nc_property_id(1, 7), nmos::fields::nc::touchpoints, value::string(U("NcTouchpoint")), true, true, true, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Runtime property constraints")), details::make_nc_property_id(1, 8), nmos::fields::nc::runtime_property_constraints, value::string(U("NcPropertyConstraints")), true, true, true, false, value::null())); - auto methods = value::array(); - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Get property value")), details::make_nc_method_id(1, 1), U("Get"), U("NcMethodResultPropertyValue"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Property value")), nmos::fields::nc::value, value::null(), true, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Set property value")), details::make_nc_method_id(1, 2), U("Set"), U("NcMethodResult"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Get sequence item")), details::make_nc_method_id(1, 3), U("GetSequenceItem"), U("NcMethodResultPropertyValue"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Value")), nmos::fields::nc::value, value::null(), true, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Set sequence item value")), details::make_nc_method_id(1, 4), U("SetSequenceItem"), U("NcMethodResult"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Value")), nmos::fields::nc::value, value::null(), true, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Add item to sequence")), details::make_nc_method_id(1, 5), U("AddSequenceItem"), U("NcMethodResultId"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Delete sequence item")), details::make_nc_method_id(1, 6), U("RemoveSequenceItem"), U("NcMethodResult"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Get sequence length")), details::make_nc_method_id(1, 7), U("GetSequenceLength"), U("NcMethodResultLength"), parameters, false)); - } - auto events = value::array(); - web::json::push_back(events, details::make_nc_event_descriptor(value::string(U("Property changed event")), details::make_nc_event_id(1, 1), U("PropertyChanged"), U("NcPropertyChangedEventData"), false)); - - web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcObject class descriptor")), details::make_nc_class_id({ 1 }), U("NcObject"), value::null(), properties, methods, events)); - } - - // NcBlock control class - { - auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("TRUE if block is functional")), details::make_nc_property_id(2, 1), nmos::fields::nc::enabled, value::string(U("NcBoolean")), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Descriptors of this block's members")), details::make_nc_property_id(2, 2), nmos::fields::nc::members, value::string(U("NcBlockMemberDescriptor")), true, false, true, false, value::null())); - auto methods = value::array(); - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("If recurse is set to true, nested members can be retrieved")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Gets descriptors of members of the block")), details::make_nc_method_id(2, 1), U("GetMemberDescriptors"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Relative path to search for (MUST not include the role of the block targeted by oid)")), nmos::fields::nc::path, value::string(U("NcRolePath")), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Finds member(s) by path")), details::make_nc_method_id(2, 2), U("FindMembersByPath"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Role text to search for")), nmos::fields::nc::role, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Signals if the comparison should be case sensitive")), nmos::fields::nc::case_sensitive, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("TRUE to only return exact matches")), nmos::fields::nc::match_whole_string, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("TRUE to search nested blocks")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Finds members with given role name or fragment")), details::make_nc_method_id(2, 3), U("FindMembersByRole"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Class id to search for")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("If TRUE it will also include derived class descriptors")), nmos::fields::nc::include_derived, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("TRUE to search nested blocks")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Finds members with given class id")), details::make_nc_method_id(2, 4), U("FindMembersByClassId"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); - } - auto events = value::array(); - - web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcBlock class descriptor")), details::make_nc_class_id({ 1, 1 }), U("NcBlock"), value::null(), properties, methods, events)); - } - - // NcWorker control class - { - auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("TRUE iff worker is enabled")), details::make_nc_property_id(2, 1), nmos::fields::nc::enabled, value::string(U("NcBoolean")), false, false, false, false, value::null())); - auto methods = value::array(); - auto events = value::array(); - - web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcWorker class descriptor")), details::make_nc_class_id({ 1, 2 }), U("NcWorker"), value::null(), properties, methods, events)); - } - - // NcManager control class - { - auto properties = value::array(); - auto methods = value::array(); - auto events = value::array(); - - web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcManager class descriptor")), details::make_nc_class_id({ 1, 3 }), U("NcManager"), value::null(), properties, methods, events)); - } - - // NcDeviceManager control class - { - auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Version of MS-05-02 that this device uses")), details::make_nc_property_id(3, 1), nmos::fields::nc::nc_version, value::string(U("NcVersionCode")), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Manufacturer descriptor")), details::make_nc_property_id(3, 2), nmos::fields::nc::manufacturer, value::string(U("NcManufacturer")), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Product descriptor")), details::make_nc_property_id(3, 3), nmos::fields::nc::product, value::string(U("NcProduct")), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Serial number")), details::make_nc_property_id(3, 4), nmos::fields::nc::serial_number, value::string(U("NcString")), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Asset tracking identifier (user specified)")), details::make_nc_property_id(3, 5), nmos::fields::nc::user_inventory_code, value::string(U("NcString")), false, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Name of this device in the application. Instance name, not product name")), details::make_nc_property_id(3, 6), nmos::fields::nc::device_name, value::string(U("NcString")), false, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Role of this device in the application")), details::make_nc_property_id(3, 7), nmos::fields::nc::device_role, value::string(U("NcString")), false, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Device operational state")), details::make_nc_property_id(3, 8), nmos::fields::nc::operational_state, value::string(U("NcDeviceOperationalState")), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Reason for most recent reset")), details::make_nc_property_id(3, 9), nmos::fields::nc::reset_cause, value::string(U("NcResetCause")), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Arbitrary message from dev to controller")), details::make_nc_property_id(3, 10), nmos::fields::nc::message, value::string(U("NcString")), true, true, false, false, value::null())); - auto methods = value::array(); - auto events = value::array(); - - web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcDeviceManager class descriptor")), details::make_nc_class_id({ 1, 3, 1 }), U("NcDeviceManager"), value::string(U("DeviceManager")), properties, methods, events)); - } - - // NcClassManager control class - { - auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Descriptions of all control classes in the device (descriptors do not contain inherited elements)")), details::make_nc_property_id(3, 1), nmos::fields::nc::control_classes, value::string(U("NcClassDescriptor")), true, false, true, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(value::string(U("Descriptions of all data types in the device (descriptors do not contain inherited elements)")), details::make_nc_property_id(3, 2), nmos::fields::nc::datatypes, value::string(U("NcDatatypeDescriptor")), true, false, true, false, value::null())); - auto methods = value::array(); - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("class ID")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("If set the descriptor would contain all inherited elements")), nmos::fields::nc::include_inherited, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Get a single class descriptor")), details::make_nc_method_id(3, 1), U("GetControlClass"), U("NcMethodResultClassDescriptor"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("name of datatype")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("If set the descriptor would contain all inherited elements")), nmos::fields::nc::include_inherited, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Get a single datatype descriptor")), details::make_nc_method_id(3, 2), U("GetDatatype"), U("NcMethodResultDatatypeDescriptor"), parameters, false)); - } - auto events = value::array(); - - web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcClassManager class descriptor")), details::make_nc_class_id({ 1, 3, 2 }), U("NcClassManager"), value::string(U("ClassManager")), properties, methods, events)); - } - - // load the minimal datatypes - data[nmos::fields::nc::datatypes] = value::array(); - auto& datatypes = data[nmos::fields::nc::datatypes]; - - // NcObject datatypes - // NcClassId - web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Sequence of class ID fields")), U("NcClassId"), value::null(), U("NcInt32"), true)); - // NcOid - web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Object id")), U("NcOid"), value::null(), U("NcUint32"), false)); - // NcTouchpoint - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Context namespace")), nmos::fields::nc::context_namespace, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Base touchpoint class")), U("NcTouchpoint"), value::null(), fields, value::null())); - } - // NcElementId - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Level of the element")), nmos::fields::nc::level, value::string(U("NcUint16")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Index of the element")), nmos::fields::nc::index, value::string(U("NcUint16")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Class element id which contains the level and index")), U("NcElementId"), value::null(), fields, value::null())); - } - // NcPropertyId - { - auto fields = value::array(); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Property id which contains the level and index")), U("NcPropertyId"), value::null(), fields, value::string(U("NcElementId")))); - } - // NcPropertyConstraints - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("The id of the property being constrained")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional default value")), nmos::fields::nc::default_value, value::null(), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Property constraints class")), U("NcPropertyConstraints"), value::null(), fields, value::null())); - } - // NcMethodResultPropertyValue - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Getter method value for the associated property")), nmos::fields::nc::value, value::null(), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Result when invoking the getter method associated with a property")), U("NcMethodResultPropertyValue"), value::null(), fields, value::string(U("NcMethodResult")))); - } - // NcMethodStatus - { - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful")), U("Ok"), 200)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful but targeted property is deprecated")), U("PropertyDeprecated"), 298)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful but method is deprecated")), U("MethodDeprecated"), 299)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Badly-formed command (e.g. the incoming command has invalid message encoding and cannot be parsed by the underlying protocol)")), U("BadCommandFormat"), 400)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Client is not authorized")), U("Unauthorized"), 401)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Command addresses a nonexistent object")), U("BadOid"), 404)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Attempt to change read-only state")), U("Readonly"), 405)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call is invalid in current operating context (e.g. attempting to invoke a method when the object is disabled)")), U("InvalidRequest"), 406)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("There is a conflict with the current state of the device")), U("Conflict"), 409)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Something was too big")), U("BufferOverflow"), 413)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Index is outside the available range")), U("IndexOutOfBounds"), 414)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method parameter does not meet expectations (e.g. attempting to invoke a method with an invalid type for one of its parameters)")), U("ParameterError"), 417)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed object is locked")), U("Locked"), 423)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Internal device error")), U("DeviceError"), 500)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed method is not implemented by the addressed object")), U("MethodNotImplemented"), 501)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed property is not implemented by the addressed object")), U("PropertyNotImplemented"), 502)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("The device is not ready to handle any commands")), U("NotReady"), 503)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call did not finish within the allotted time")), U("Timeout"), 504)); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Method invokation status")), U("NcMethodStatus"), value::null(), items)); - } - // NcMethodResult - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Status for the invoked method")), nmos::fields::nc::status, value::string(U("NcMethodStatus")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Base result of the invoked method")), U("NcMethodResult"), value::null(), fields, value::null())); - } - // NcId - web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Identity handler")), U("NcId"), value::null(), U("NcUint32"), false)); - // NcMethodResultId - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Id result value")), nmos::fields::nc::value, value::string(U("NcId")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Id method result")), U("NcMethodResultId"), value::null(), fields, value::string(U("NcMethodResult")))); - } - // NcMethodResultLength - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Length result value")), nmos::fields::nc::value, value::string(U("NcUint32")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Length method result")), U("NcMethodResultLength"), value::null(), fields, value::string(U("NcMethodResult")))); - } - // NcPropertyChangeType - { - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Current value changed")), U("ValueChanged"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item added")), U("SequenceItemAdded"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item changed")), U("SequenceItemChanged"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item removed")), U("SequenceItemRemoved"), 3)); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Type of property change")), U("NcPropertyChangeType"), value::null(), items)); - } - // NcPropertyChangedEventData - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("The id of the property that changed")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Information regarding the change type")), nmos::fields::nc::change_type, value::string(U("NcPropertyChangeType")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property-type specific value")), nmos::fields::nc::value, value::null(), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Index of sequence item if the property is a sequence")), nmos::fields::nc::sequence_item_index, value::string(U("NcId")), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Payload of property-changed event")), U("NcPropertyChangedEventData"), value::null(), fields, value::null())); - } - - // NcBlock datatypes - // NcDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional user facing description")), nmos::fields::nc::description, value::string(U("NcString")), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Base descriptor")), U("NcDescriptor"), value::null(), fields, value::null())); - } - // NcBlockMemberDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Role of member in its containing block")), nmos::fields::nc::role, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("OID of member")), nmos::fields::nc::oid, value::string(U("NcOid")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff member's OID is hardwired into device")), nmos::fields::nc::constant_oid, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Class ID")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("User label")), nmos::fields::nc::user_label, value::string(U("NcString")), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Containing block's OID")), nmos::fields::nc::owner, value::string(U("NcOid")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor which is specific to a block member")), U("NcBlockMemberDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); - } - // NcMethodResultBlockMemberDescriptors - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Block member descriptors method result value")), nmos::fields::nc::value, value::string(U("NcBlockMemberDescriptor")), false, true, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing block member descriptors as the value")), U("NcMethodResultBlockMemberDescriptors"), value::null(), fields, value::string(U("NcMethodResult")))); - } - - // NcWorker has no datatypes - - // NcManager has no datatypes - - // NcDeviceManager datatypes - // NcVersionCode - web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Version code in semantic versioning format")), U("NcVersionCode"), value::null(), U("NcString"), false)); - // NcOrganizationId - web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Unique 24-bit organization id")), U("NcOrganizationId"), value::null(), U("NcInt32"), false)); - // NcUri - web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Uniform resource identifier")), U("NcUri"), value::null(), U("NcString"), false)); - // NcManufacturer - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's name")), nmos::fields::nc::name, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("IEEE OUI or CID of manufacturer")), nmos::fields::nc::organization_id, value::string(U("NcOrganizationId")), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("URL of the manufacturer's website")), nmos::fields::nc::website, value::string(U("NcUri")), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Manufacturer descriptor")), U("NcManufacturer"), value::null(), fields, value::null())); - } - // NcUuid - web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("UUID")), U("NcUuid"), value::null(), U("NcString"), false)); - // NcProduct - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Product name")), nmos::fields::nc::name, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's unique key to product - model number, SKU, etc")), nmos::fields::nc::key, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's product revision level code")), nmos::fields::nc::revision_level, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Brand name under which product is sold")), nmos::fields::nc::brand_name, value::string(U("NcString")), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Unique UUID of product (not product instance)")), nmos::fields::nc::uuid, value::string(U("NcUuid")), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Text description of product")), nmos::fields::nc::description, value::string(U("NcString")), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Product descriptor")), U("NcProduct"), value::null(), fields, value::null())); - } - // NcDeviceGenericState - { - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Normal operation")), U("NormalOperation"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is initializing")), U("Initializing"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is performing a software or firmware update")), U("Updating"), 3)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is experiencing a licensing error")), U("LicensingError"), 4)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is experiencing an internal error")), U("InternalError"), 5)); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Device generic operational state")), U("NcDeviceGenericState"), value::null(), items)); - } - // NcDeviceOperationalState - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Generic operational state")), nmos::fields::nc::generic_state, value::string(U("NcDeviceGenericState")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Specific device details")), nmos::fields::nc::device_specific_details, value::string(U("NcString")), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Device operational state")), U("NcDeviceOperationalState"), value::null(), fields, value::null())); - } - // NcResetCause - { - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Power on")), U("PowerOn"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Internal error")), U("InternalError"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Upgrade")), U("Upgrade"), 3)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Controller request")), U("ControllerRequest"), 4)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Manual request from the front panel")), U("ManualReset"), 5)); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Reset cause enum")), U("NcResetCause"), value::null(), items)); - } - - // NcClassManager datatypes - // NcName - web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Programmatically significant name, alphanumerics + underscore, no spaces")), U("NcName"), value::null(), U("NcString"), false)); - // NcPropertyDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property id with level and index")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of property")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of property's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is read-only")), nmos::fields::nc::is_read_only, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class property")), U("NcPropertyDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); - } - // NcMethodId - { - auto fields = value::array(); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Method id which contains the level and index")), U("NcMethodId"), value::null(), fields, value::string(U("NcElementId")))); - } - // NcParameterDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of parameter")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of parameter's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a method parameter")), U("NcParameterDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); - } - // NcMethodDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Method id with level and index")), nmos::fields::nc::id, value::string(U("NcMethodId")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of method")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of method result's datatype")), nmos::fields::nc::result_datatype, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Parameter descriptors if any")), nmos::fields::nc::parameters, value::string(U("NcParameterDescriptor")), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class method")), U("NcMethodDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); - } - // NcEventId - { - auto fields = value::array(); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Event id which contains the level and index")), U("NcEventId"), value::null(), fields, value::string(U("NcElementId")))); - } - // NcEventDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Event id with level and index")), nmos::fields::nc::id, value::string(U("NcEventId")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of event")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of event data's datatype")), nmos::fields::nc::event_datatype, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class event")), U("NcEventDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); - } - // NcClassDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Identity of the class")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of the class")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Role if the class has fixed role (manager classes)")), nmos::fields::nc::fixed_role, value::string(U("NcString")), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property descriptors")), nmos::fields::nc::properties, value::string(U("NcPropertyDescriptor")), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Method descriptors")), nmos::fields::nc::methods, value::string(U("NcMethodDescriptor")), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Event descriptors")), nmos::fields::nc::events, value::string(U("NcEventDescriptor")), false, true, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class")), U("NcClassDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); - } - // NcParameterConstraints - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Default value")), nmos::fields::nc::default_value, value::null(), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Abstract parameter constraints class")), U("NcParameterConstraints"), value::null(), fields, value::null())); - } - // NcDatatypeType - { - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Primitive datatype")), U("Primitive"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Simple alias of another datatype")), U("Typedef"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Data structure")), U("Struct"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Enum datatype")), U("Enum"), 3)); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Datatype type")), U("NcDatatypeType"), value::null(), items)); - } - // NcDatatypeDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Datatype name")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Type: Primitive, Typedef, Struct, Enum")), nmos::fields::nc::type, value::string(U("NcDatatypeType")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Base datatype descriptor")), U("NcDatatypeDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); - } - // NcMethodResultClassDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Class descriptor method result value")), nmos::fields::nc::value, value::string(U("NcClassDescriptor")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing a class descriptor as the value")), U("NcMethodResultClassDescriptor"), value::null(), fields, value::string(U("NcMethodResult")))); - } - // NcMethodResultDatatypeDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Datatype descriptor method result value")), nmos::fields::nc::value, value::string(U("NcDatatypeDescriptor")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing a datatype descriptor as the value")), U("NcMethodResultDatatypeDescriptor"), value::null(), fields, value::string(U("NcMethodResult")))); - } - - return data; - } - } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager nmos::resource make_device_manager(details::nc_oid oid, nmos::resource& root_block, const nmos::settings& settings) { @@ -942,8 +45,8 @@ namespace nmos auto data = details::make_nc_class_manager(oid, owner, user_label); // add NcClassManager block_member_descriptor to root block members - web::json::push_back(root_block_data[nmos::fields::nc::members], details::make_nc_block_member_descriptor( - description, nmos::fields::nc::role(data), oid, nmos::fields::nc::constant_oid(data), data.at(nmos::fields::nc::class_id), user_label, owner)); + web::json::push_back(root_block_data[nmos::fields::nc::members], + details::make_nc_block_member_descriptor(description, nmos::fields::nc::role(data), oid, nmos::fields::nc::constant_oid(data), data.at(nmos::fields::nc::class_id), user_label, owner)); return{ is12_versions::v1_0, types::nc_class_manager, std::move(data), true }; } @@ -953,7 +56,7 @@ namespace nmos { using web::json::value; - auto data = details::make_nc_block(1, true, value::null(), U("root"), value::string(U("Root")), value::null(), value::null(), true, value::array()); + auto data = details::make_nc_block(details::nc_block_class_id, 1, true, value::null(), U("root"), value::string(U("Root")), value::null(), value::null(), true, value::array()); return{ is12_versions::v1_0, types::nc_block, std::move(data), true }; } diff --git a/Development/nmos/control_protocol_resources.h b/Development/nmos/control_protocol_resources.h index f7683c2ae..ca16c8d9b 100644 --- a/Development/nmos/control_protocol_resources.h +++ b/Development/nmos/control_protocol_resources.h @@ -2,141 +2,13 @@ #define NMOS_CONTROL_PROTOCOL_RESOURCES_H #include +#include "nmos/control_protocol_resource.h" // for details::nc_oid definition #include "nmos/settings.h" -namespace web -{ - namespace json - { - class value; - } -} - namespace nmos { struct resource; - namespace details - { - namespace nc_message_type - { - enum type - { - command = 0, - command_response = 1, - notification = 2, - subscription = 3, - subscription_response = 4, - error = 5 - }; - } - - // Method invokation status - namespace nc_method_status - { - enum status - { - ok = 200, // Method call was successful - property_deprecated = 298, // Method call was successful but targeted property is deprecated - method_deprecated = 299, // Method call was successful but method is deprecated - bad_command_format = 400, // Badly-formed command - unathorized = 401, // Client is not authorized - bad_oid = 404, // Command addresses a nonexistent object - read_only = 405, // Attempt to change read-only state - invalid_request = 406, // Method call is invalid in current operating context - conflict = 409, // There is a conflict with the current state of the device - buffer_overflow = 413, // Something was too big - parameter_error = 417, // Method parameter does not meet expectations - locked = 423, // Addressed object is locked - device_error = 500, // Internal device error - method_not_implemented = 501, // Addressed method is not implemented by the addressed object - property_not_implemented = 502, // Addressed property is not implemented by the addressed object - not_ready = 503, // The device is not ready to handle any commands - timeout = 504, // Method call did not finish within the allotted time - property_version_error = 505 // Incompatible protocol version - }; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodresult - struct nc_method_result - { - nc_method_status::status status; - }; - - // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-response-message-type - web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message); - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result); - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value); - - // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-message-type - web::json::value make_control_protocol_message_response(nc_message_type::type type, const web::json::value& responses); - // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#error-messages - web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message); - - // Datatype type - namespace nc_datatype_type - { - enum type - { - Primitive = 0, - Typedef = 1, - Struct = 2, - Enum = 3 - }; - } - - // Device generic operational state - namespace nc_device_generic_state - { - enum state - { - Unknown = 0, // Unknown - NormalOperation = 1, // Normal operation - Initializing = 2, // Device is initializing - Updating = 3, // Device is performing a software or firmware update - LicensingError = 4, // Device is experiencing a licensing error - InternalError = 5 // Device is experiencing an internal error - }; - } - - // Reset cause enum - namespace nc_reset_cause - { - enum cause - { - Unknown = 0, // 0 Unknown - Power_on = 1, // 1 Power on - InternalError = 2, // 2 Internal error - Upgrade = 3, // 3 Upgrade - Controller_request = 4, // 4 Controller request - ManualReset = 5 // 5 Manual request from the front panel - }; - } - - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncid - typedef uint32_t nc_id; - - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncoid - typedef uint32_t nc_oid; - - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncuri - typedef utility::string_t nc_uri; - - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncuuid - typedef utility::string_t nc_uuid; - - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid - typedef std::vector nc_class_id; - - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nctouchpoint - typedef utility::string_t nc_touch_point; - - typedef std::map properties; - - typedef std::function method; - typedef std::map methods; // method_id vs method handler - } - nmos::resource make_device_manager(details::nc_oid oid, nmos::resource& root_block, const nmos::settings& settings); nmos::resource make_class_manager(details::nc_oid oid, nmos::resource& root_block); diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp new file mode 100644 index 000000000..c420072be --- /dev/null +++ b/Development/nmos/control_protocol_state.cpp @@ -0,0 +1,23 @@ +#include "nmos/control_protocol_state.h" + +#include "nmos/control_protocol_resource.h" + +namespace nmos +{ + namespace experimental + { + control_protocol_state::control_protocol_state() + { + // setup the core control classes (properties/methods/events) + control_classes = + { + { details::make_nc_class_id(details::nc_object_class_id), { details::make_nc_object_properties(), details::make_nc_object_methods(), details::make_nc_object_events() } }, + { details::make_nc_class_id(details::nc_block_class_id), { details::make_nc_block_properties(), details::make_nc_block_methods(), details::make_nc_block_events() } }, + { details::make_nc_class_id(details::nc_worker_class_id), { details::make_nc_worker_properties(), details::make_nc_worker_methods(), details::make_nc_worker_events() } }, + { details::make_nc_class_id(details::nc_manager_class_id), { details::make_nc_manager_properties(), details::make_nc_manager_methods(), details::make_nc_manager_events() } }, + { details::make_nc_class_id(details::nc_device_manager_class_id), { details::make_nc_device_manager_properties(), details::make_nc_device_manager_methods(), details::make_nc_device_manager_events() } }, + { details::make_nc_class_id(details::nc_class_manager_class_id), { details::make_nc_class_manager_properties(), details::make_nc_class_manager_methods(), details::make_nc_class_manager_events() } } + }; + } + } +} \ No newline at end of file diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h new file mode 100644 index 000000000..5a8b5e1ca --- /dev/null +++ b/Development/nmos/control_protocol_state.h @@ -0,0 +1,36 @@ +#ifndef NMOS_CONTROL_PROTOCOL_STATE_H +#define NMOS_CONTROL_PROTOCOL_STATE_H + +#include +#include "cpprest/json_utils.h" +#include "nmos/mutex.h" + +namespace nmos +{ + namespace experimental + { + struct control_class + { + web::json::value properties; // array of nc_property_descriptor + web::json::value methods; // array of nc_method_descriptor + web::json::value events; // array of nc_event_descriptor + }; + + typedef std::map control_classes; + + struct control_protocol_state + { + // mutex to be used to protect the members from simultaneous access by multiple threads + mutable nmos::mutex mutex; + + control_classes control_classes; + + nmos::read_lock read_lock() const { return nmos::read_lock{ mutex }; } + nmos::write_lock write_lock() const { return nmos::write_lock{ mutex }; } + + control_protocol_state(); + }; + } +} + +#endif diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 6bb263468..6639bcc93 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -45,6 +45,226 @@ namespace nmos { controlprotocol_validator().validate(request_data, experimental::make_controlprotocolapi_subscription_message_schema_uri(version)); } + + std::pair create_properties_methods(nmos::node_model& model, const nc_class_id& class_id_, const nmos::experimental::control_classes& control_classes) + { + using web::json::value; + using web::json::value_of; + + // hmm, methods should also be passing in via the control_class::methods + + // NcObject methods implementation + // get property + auto get = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto& resources = model.control_protocol_resources; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + // where arguments is the property id = (level, index) + const auto& property_id = nmos::fields::nc::id(arguments); + + // find the relevant nc_property_descriptor + auto property_found = std::find_if(properties.begin(), properties.end(), [property_id](const web::json::value& property) + { + return property_id == nmos::fields::nc::id(property); + }); + + if (property_found != properties.end()) + { + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(*property_found))); + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << " to do get"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + } + + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid << " to do get"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + }; + // set property + auto set = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto& resources = model.control_protocol_resources; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& val = nmos::fields::nc::value(arguments); + + // find the relevant nc_property_descriptor + auto property_found = std::find_if(properties.begin(), properties.end(), [property_id](const web::json::value& property) + { + return property_id == nmos::fields::nc::id(property); + }); + if (property_found != properties.end()) + { + if (!nmos::fields::nc::is_read_only(*property_found)) + { + resources.modify(resource, [&](nmos::resource& resource) + { + resource.data[nmos::fields::nc::name(*property_found)] = val; + + resource.updated = strictly_increasing_update(resources); + }); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); + } + else + { + return details::make_control_protocol_response(handle, { details::nc_method_status::read_only }); + } + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << " to do set"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + } + + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid << " to do set"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + }; + + // NcBlock methods implementation + // get descriptors of members of the block + auto get_member_descriptors = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto& resources = model.control_protocol_resources; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + // where arguments is the boolean recurse value + // hmm, If recurse is set to true, nested members is to be retrieved + const auto& recurse = nmos::fields::nc::recurse(arguments); + + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, resource->data.at(nmos::fields::nc::members)); + } + + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid << " to get member descriptors"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + }; + + // NcClassManager methods implementation + auto get_control_class = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + { + // hmm, todo + + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid << " to get control class"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + }; + + // method handlers for the different classes + details::methods nc_object_method_handlers; // method_id vs NcObject method_handler + details::methods nc_block_method_handlers; // method_id vs NcBlock method_handler + details::methods nc_worker_method_handlers; // method_id vs NcWorker method_handler + details::methods nc_manager_method_handlers; // method_id vs NcManager method_handler + details::methods nc_device_manager_method_handlers; // method_id vs NcDeviceManager method_handler + details::methods nc_class_manager_method_handlers; // method_id vs NcClassManager method_handler + + // NcObject methods + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject + nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 1 } })] = get; + nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 2 } })] = set; + //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 3 } })] = get_sequence_item; + //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 4 } })] = set_sequence_item; + //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 5 } })] = add_sequence_item; + //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 6 } })] = remove_sequence_item; + //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 7 } })] = get_sequence_length; + + // NcBlock methods + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 1 } })] = get_member_descriptors; + //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 2 } })] = find_members_by_path; + //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 3 } })] = find_members_by_role; + //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 4 } })] = find_members_by_class_id; + + // NcWorker has no extended method + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker + + // NcManager has no extended method + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager + + // NcDeviceManger has no extended method + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + + // NcClassManager methods + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 1 } })] = get_control_class; + //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 2 } })] = get_datatype; + + value properties = value::array(); // combined base classes nc_property_descriptor(s) for the required class_id + details::methods methods; // list of combined base classes method handlers + + auto found_class = control_classes.find(make_nc_class_id(class_id_)); + if (control_classes.end() != found_class) + { + // hmm, update the array of properties, will be updated the list of method handlers + auto insert_properties = [&properties, &control_classes](const nc_class_id& class_id_) + { + auto class_id = make_nc_class_id(class_id_); + auto found = control_classes.find(class_id); + if (control_classes.end() != found) + { + auto& nc_class_properties = found->second.properties.as_array(); + for (auto& nc_class_property : nc_class_properties) + { + web::json::push_back(properties, nc_class_property); + } + } + }; + + auto class_id = class_id_; + while (class_id.size()) + { + insert_properties(class_id); + + // hmm, to be deleted, once the methods are passed in + if (details::nc_object_class_id == class_id) + { + methods.insert(nc_object_method_handlers.begin(), nc_object_method_handlers.end()); + } + else if (details::nc_block_class_id == class_id) + { + methods.insert(nc_block_method_handlers.begin(), nc_block_method_handlers.end()); + } + else if (details::nc_device_manager_class_id == class_id) + { + methods.insert(nc_manager_method_handlers.begin(), nc_manager_method_handlers.end()); + } + else if (details::nc_device_manager_class_id == class_id) + { + methods.insert(nc_device_manager_method_handlers.begin(), nc_device_manager_method_handlers.end()); + } + else if (details::nc_class_manager_class_id == class_id) + { + methods.insert(nc_class_manager_method_handlers.begin(), nc_class_manager_method_handlers.end()); + } + class_id.pop_back(); + } + } + else + { + throw std::runtime_error("unknown control class"); + } + + return { properties, methods }; + } } // IS-12 Control Protocol WebSocket API @@ -183,273 +403,11 @@ namespace nmos }; } - web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate_) + web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_classes_handler get_control_protocol_classes, slog::base_gate& gate_) { using web::json::value; - using web::json::value_of; - - // NcObject properties - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject - const details::properties nc_object_properties = - { - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject - { value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 1 } }), nmos::fields::nc::class_id }, - { value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 2 } }), nmos::fields::nc::oid }, - { value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 3 } }), nmos::fields::nc::constant_oid }, - { value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 4 } }), nmos::fields::nc::owner }, - { value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 5 } }), nmos::fields::nc::role }, - { value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 6 } }), nmos::fields::nc::user_label }, - { value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 7 } }), nmos::fields::nc::touchpoints }, - { value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 8 } }), nmos::fields::nc::runtime_property_constraints } - }; - - // NcBlock properties - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock - const details::properties nc_block_properties = - { - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock - { value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 1 } }), nmos::fields::nc::enabled }, - { value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 2 } }), nmos::fields::nc::members } - }; - - // NcWorker properties - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker - const details::properties nc_worker_properties = - { - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker - { value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 1 } }), nmos::fields::nc::enabled } - }; - - // NcManager has no property - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager - const details::properties nc_manager_properties; - - // NcDeviceManager properties - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager - const details::properties nc_device_manager_properties = - { - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager - { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 1 } }), nmos::fields::nc::nc_version }, - { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 2 } }), nmos::fields::nc::manufacturer }, - { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 3 } }), nmos::fields::nc::product }, - { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 4 } }), nmos::fields::nc::serial_number }, - { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 5 } }), nmos::fields::nc::user_inventory_code }, - { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 6 } }), nmos::fields::nc::device_name }, - { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 7 } }), nmos::fields::nc::device_role }, - { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 8 } }), nmos::fields::nc::operational_state }, - { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 9 } }), nmos::fields::nc::reset_cause }, - { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 10 } }), nmos::fields::nc::message } - }; - - // NcClassManager properties - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - const details::properties nc_class_manager_properties = - { - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 1 } }), nmos::fields::nc::control_classes }, - { value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 2 } }), nmos::fields::nc::datatypes } - }; - - // method handlers for the different classes - details::methods nc_object_method_handlers; // method_id vs NcObject method_handler - details::methods nc_block_method_handlers; // method_id vs NcBlock method_handler - details::methods nc_worker_method_handlers; // method_id vs NcWorker method_handler - details::methods nc_manager_method_handlers; // method_id vs NcManager method_handler - details::methods nc_device_manager_method_handlers; // method_id vs NcDeviceManager method_handler - details::methods nc_class_manager_method_handlers; // method_id vs NcClassManager method_handler - - // NcObject methods implementation - // get property - auto get = [&model](const details::properties& properties, int32_t handle, int32_t oid, const value& arguments) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto& resources = model.control_protocol_resources; - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != resource) - { - // where arguments is the property id = (level, index) - const auto& property_id = nmos::fields::nc::id(arguments); - - // is property_id defined in properties map - auto property_found = properties.find(property_id); - if (property_found != properties.end()) - { - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, resource->data.at(property_found->second)); - } - - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << " to do get"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); - } - - // resource not found for the given oid - utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to do get"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - }; - // set property - auto set = [&model](const details::properties& properties, int32_t handle, int32_t oid, const value& arguments) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto& resources = model.control_protocol_resources; - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != resource) - { - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& val = nmos::fields::nc::value(arguments); - - // hmm, todo check property_id allowed in resource's class_id - - // is property_id defined in properties map - auto property_found = properties.find(property_id); - if (property_found != properties.end()) - { - resources.modify(resource, [&](nmos::resource& resource) - { - resource.data[property_found->second] = val; - - resource.updated = strictly_increasing_update(resources); - }); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); - } - else - { - // hmm, find property function from user properties map - } - - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << " to do set"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); - } - - // resource not found for the given oid - utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to do set"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - }; - - // NcBlock methods implementation - // get descriptors of members of the block - auto get_member_descriptors = [&model](const details::properties& properties, int32_t handle, int32_t oid, const value& arguments) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto& resources = model.control_protocol_resources; - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != resource) - { - // where arguments is the boolean recurse value - // hmm, If recurse is set to true, nested members is to be retrieved - const auto& recurse = nmos::fields::nc::recurse(arguments); - - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, resource->data.at(nmos::fields::nc::members)); - } - - // resource not found for the given oid - utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to get member descriptors"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - }; - - // NcClassManager methods implementation - auto get_control_class = [&model](const details::properties& properties, int32_t handle, int32_t oid, const value& arguments) - { - // resource not found for the given oid - utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to get control class"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - }; - - // NcObject methods - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject - nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 1 } })] = get; - nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 2 } })] = set; - //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 3 } })] = get_sequence_item; - //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 4 } })] = set_sequence_item; - //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 5 } })] = add_sequence_item; - //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 6 } })] = remove_sequence_item; - //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 7 } })] = get_sequence_length; - - // NcBlock methods - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock - nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 1 } })] = get_member_descriptors; - //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 2 } })] = find_members_by_path; - //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 3 } })] = find_members_by_role; - //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 4 } })] = find_members_by_class_id; - - // NcWorker has no extended method - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker - - // NcManager has no extended method - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager - - // NcDeviceManger has no extended method - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager - - // NcClassManager methods - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 1 } })] = get_control_class; - //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 2 } })] = get_datatype; - - // create properties and method handlers based on resource type - auto create_properties_methods = [=](const nmos::type& type) - { - details::properties properties; - details::methods methods; - - // all start from NcObject - properties.insert(nc_object_properties.begin(), nc_object_properties.end()); - methods.insert(nc_object_method_handlers.begin(), nc_object_method_handlers.end()); - if (type == nmos::types::nc_block) - { - properties.insert(nc_block_properties.begin(), nc_block_properties.end()); - methods.insert(nc_block_method_handlers.begin(), nc_block_method_handlers.end()); - } - else if (type == nmos::types::nc_worker) - { - properties.insert(nc_worker_properties.begin(), nc_worker_properties.end()); - methods.insert(nc_worker_method_handlers.begin(), nc_worker_method_handlers.end()); - } - else if (type == nmos::types::nc_manager) - { - properties.insert(nc_manager_properties.begin(), nc_manager_properties.end()); - methods.insert(nc_manager_method_handlers.begin(), nc_manager_method_handlers.end()); - } - else if (type == nmos::types::nc_device_manager) - { - properties.insert(nc_manager_properties.begin(), nc_manager_properties.end()); - properties.insert(nc_device_manager_properties.begin(), nc_device_manager_properties.end()); - methods.insert(nc_manager_method_handlers.begin(), nc_manager_method_handlers.end()); - methods.insert(nc_device_manager_method_handlers.begin(), nc_device_manager_method_handlers.end()); - } - else if (type == nmos::types::nc_class_manager) - { - properties.insert(nc_manager_properties.begin(), nc_manager_properties.end()); - properties.insert(nc_class_manager_properties.begin(), nc_class_manager_properties.end()); - methods.insert(nc_manager_method_handlers.begin(), nc_manager_method_handlers.end()); - methods.insert(nc_class_manager_method_handlers.begin(), nc_class_manager_method_handlers.end()); - } - - // hmm, add user properties - //if (!user_properties.empty()) - //{ - // properties.insert(user_properties.begin(), user_properties.end()); - //} - - // hmm, add user method handlers - //if (!user_methods.empty()) - //{ - // methods.insert(user_methods.begin(), user_methods.end()); - //} - - return std::pair(properties, methods); - }; - return [&model, &websockets, create_properties_methods, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id, const web::websockets::websocket_incoming_message& msg_) + return [&model, &websockets, get_control_protocol_classes, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id, const web::websockets::websocket_incoming_message& msg_) { nmos::ws_api_gate gate(gate_, connection_uri); @@ -508,8 +466,9 @@ namespace nmos auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != resource) { - // create properties and method handlers based on resource type - auto properties_methods = create_properties_methods(resource->type); + // create the combined properties and method handlers based on class_id + auto class_id = details::parse_nc_class_id(resource->data.at(nmos::fields::nc::class_id)); + auto properties_methods = details::create_properties_methods(model, class_id, get_control_protocol_classes()); auto& properties = properties_methods.first; auto& methods = properties_methods.second; @@ -518,7 +477,7 @@ namespace nmos if (method != methods.end()) { // execute the relevant method handler, then accumulating up their response to reponses - web::json::push_back(responses, method->second(properties, handle, oid, arguments)); + web::json::push_back(responses, method->second(properties.as_array(), handle, oid, arguments)); } else { diff --git a/Development/nmos/control_protocol_ws_api.h b/Development/nmos/control_protocol_ws_api.h index 2371616d1..61c434f38 100644 --- a/Development/nmos/control_protocol_ws_api.h +++ b/Development/nmos/control_protocol_ws_api.h @@ -1,6 +1,7 @@ #ifndef NMOS_CONTROL_PROTOCOL_WS_API_H #define NMOS_CONTROL_PROTOCOL_WS_API_H +#include "nmos/control_protocol_handlers.h" #include "nmos/websockets.h" namespace slog @@ -15,15 +16,15 @@ namespace nmos web::websockets::experimental::listener::validate_handler make_control_protocol_ws_validate_handler(nmos::node_model& model, slog::base_gate& gate); web::websockets::experimental::listener::open_handler make_control_protocol_ws_open_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate); web::websockets::experimental::listener::close_handler make_control_protocol_ws_close_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate); - web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate); + web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_classes_handler get_control_protocol_classes, slog::base_gate& gate); - inline web::websockets::experimental::listener::websocket_listener_handlers make_control_protocol_ws_api(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate) + inline web::websockets::experimental::listener::websocket_listener_handlers make_control_protocol_ws_api(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_classes_handler get_control_protocol_classes, slog::base_gate& gate) { return{ nmos::make_control_protocol_ws_validate_handler(model, gate), nmos::make_control_protocol_ws_open_handler(model, websockets, gate), nmos::make_control_protocol_ws_close_handler(model, websockets, gate), - nmos::make_control_protocol_ws_message_handler(model, websockets, gate) + nmos::make_control_protocol_ws_message_handler(model, websockets, get_control_protocol_classes, gate) }; } diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index 2de24d450..0ac70ee42 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -72,7 +72,7 @@ namespace nmos const auto& control_protocol_ws_port = nmos::fields::control_protocol_ws_port(node_model.settings); if (control_protocol_ws_port == events_ws_port) throw std::runtime_error("Same port used for events and control protocol websockets are not supported"); auto& control_protocol_ws_api = node_server.ws_handlers[{ {}, control_protocol_ws_port }]; - control_protocol_ws_api.first = nmos::make_control_protocol_ws_api(node_model, control_protocol_ws_api.second, gate); + control_protocol_ws_api.first = nmos::make_control_protocol_ws_api(node_model, control_protocol_ws_api.second, node_implementation.get_control_protocol_classes, gate); // Set up the listeners for each HTTP API port diff --git a/Development/nmos/node_server.h b/Development/nmos/node_server.h index dc8f4efa8..eb1b95bc7 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -6,6 +6,7 @@ #include "nmos/channelmapping_activation.h" #include "nmos/connection_api.h" #include "nmos/connection_activation.h" +#include "nmos/control_protocol_handlers.h" #include "nmos/node_behaviour.h" #include "nmos/node_system_behaviour.h" #include "nmos/ocsp_response_handler.h" @@ -24,7 +25,7 @@ namespace nmos // underlying implementation into the server instance for the NMOS Node struct node_implementation { - node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response) + node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, nmos::get_control_protocol_classes_handler get_control_protocol_classes) : load_server_certificates(std::move(load_server_certificates)) , load_dh_param(std::move(load_dh_param)) , load_ca_certificates(std::move(load_ca_certificates)) @@ -36,6 +37,7 @@ namespace nmos , set_transportfile(std::move(set_transportfile)) , connection_activated(std::move(connection_activated)) , get_ocsp_response(std::move(get_ocsp_response)) + , get_control_protocol_classes(std::move(get_control_protocol_classes)) {} // use the default constructor and chaining member functions for fluent initialization @@ -57,6 +59,7 @@ namespace nmos node_implementation& on_validate_channelmapping_output_map(nmos::details::channelmapping_output_map_validator validate_map) { this->validate_map = std::move(validate_map); return *this; } node_implementation& on_channelmapping_activated(nmos::channelmapping_activation_handler channelmapping_activated) { this->channelmapping_activated = std::move(channelmapping_activated); return *this; } node_implementation& on_get_ocsp_response(nmos::ocsp_response_handler get_ocsp_response) { this->get_ocsp_response = std::move(get_ocsp_response); return *this; } + node_implementation& on_get_control_classes(nmos::get_control_protocol_classes_handler get_control_protocol_classes) { this->get_control_protocol_classes = std::move(get_control_protocol_classes); return* this; } // deprecated, use on_validate_connection_resource_patch node_implementation& on_validate_merged(nmos::details::connection_resource_patch_validator validate_merged) { return on_validate_connection_resource_patch(std::move(validate_merged)); } @@ -86,6 +89,8 @@ namespace nmos nmos::channelmapping_activation_handler channelmapping_activated; nmos::ocsp_response_handler get_ocsp_response; + + nmos::get_control_protocol_classes_handler get_control_protocol_classes; }; // Construct a server instance for an NMOS Node, implementing the IS-04 Node API, IS-05 Connection API, IS-07 Events API From eb71ebd85a85474aa2bd9ef039bd940c4818a11e Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 8 Aug 2023 16:45:50 +0100 Subject: [PATCH 008/250] Fix declaration of nmos::experimental::control_classes for Linux --- Development/nmos/control_protocol_state.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index 5a8b5e1ca..ad884733c 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -23,7 +23,7 @@ namespace nmos // mutex to be used to protect the members from simultaneous access by multiple threads mutable nmos::mutex mutex; - control_classes control_classes; + experimental::control_classes control_classes; nmos::read_lock read_lock() const { return nmos::read_lock{ mutex }; } nmos::write_lock write_lock() const { return nmos::write_lock{ mutex }; } From 212bdf41eff2c00a9f1fe02cf92fddc8e3f0d4ab Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 8 Aug 2023 16:46:45 +0100 Subject: [PATCH 009/250] Move functions around --- .../nmos/control_protocol_resource.cpp | 130 +++++++++--------- Development/nmos/control_protocol_resource.h | 37 +++-- 2 files changed, 82 insertions(+), 85 deletions(-) diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 68f0273f4..3fddd49a4 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -70,7 +70,7 @@ namespace nmos { nmos::fields::nc::message_type, type }, { nmos::fields::nc::responses, responses } }); - }; + } // error message // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#error-messages @@ -83,7 +83,7 @@ namespace nmos { nmos::fields::nc::status, method_result.status}, { nmos::fields::nc::error_message, error_message } }); - }; + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid web::json::value make_nc_class_id(const nc_class_id& class_id) @@ -390,67 +390,6 @@ namespace nmos return data; } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject - web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) - { - using web::json::value; - - const auto id = utility::conversions::details::to_string_t(oid); -// auto data = make_resource_core(id, user_label.is_null() ? U("") : user_label.as_string(), {}); - value data; - data[nmos::fields::id] = value::string(id); // required for nmos::resource - data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); - data[nmos::fields::nc::oid] = oid; - data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); - data[nmos::fields::nc::owner] = owner; - data[nmos::fields::nc::role] = value::string(role); - data[nmos::fields::nc::user_label] = user_label; - data[nmos::fields::nc::touchpoints] = touchpoints; - data[nmos::fields::nc::runtime_property_constraints] = runtime_property_constraints; - - return data; - }; - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock - web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members) - { - using web::json::value; - - auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); - data[nmos::fields::nc::enabled] = value::boolean(enabled); - data[nmos::fields::nc::members] = members; - - return data; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager - web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) - { - return make_nc_object(class_id, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager - web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, - const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, - const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause) - { - using web::json::value; - - auto data = details::make_nc_manager(nc_device_manager_class_id, oid, true, owner, U("DeviceManager"), user_label, touchpoints, runtime_property_constraints); - data[nmos::fields::nc::nc_version] = value::string(U("v1.0")); - data[nmos::fields::nc::manufacturer] = manufacturer; - data[nmos::fields::nc::product] = product; - data[nmos::fields::nc::serial_number] = value::string(serial_number); - data[nmos::fields::nc::user_inventory_code] = user_inventory_code; - data[nmos::fields::nc::device_name] = device_name; - data[nmos::fields::nc::device_role] = device_role; - data[nmos::fields::nc::operational_state] = operational_state; - data[nmos::fields::nc::reset_cause] = reset_cause; - data[nmos::fields::nc::message] = value::null(); - - return data; - } - web::json::value make_nc_object_properties() { using web::json::value; @@ -480,8 +419,8 @@ namespace nmos } { auto parameters = value::array(); - web::json::push_back(parameters,make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(parameters,make_nc_parameter_descriptor(value::string(U("Property value")), nmos::fields::nc::value, value::null(), true, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property value")), nmos::fields::nc::value, value::null(), true, false, value::null())); web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Set property value")), make_nc_method_id(1, 2), U("Set"), U("NcMethodResult"), parameters, false)); } { @@ -697,6 +636,67 @@ namespace nmos return value::array(); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject + web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + { + using web::json::value; + + const auto id = utility::conversions::details::to_string_t(oid); +// auto data = make_resource_core(id, user_label.is_null() ? U("") : user_label.as_string(), {}); + value data; + data[nmos::fields::id] = value::string(id); // required for nmos::resource + data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); + data[nmos::fields::nc::oid] = oid; + data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); + data[nmos::fields::nc::owner] = owner; + data[nmos::fields::nc::role] = value::string(role); + data[nmos::fields::nc::user_label] = user_label; + data[nmos::fields::nc::touchpoints] = touchpoints; + data[nmos::fields::nc::runtime_property_constraints] = runtime_property_constraints; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members) + { + using web::json::value; + + auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); + data[nmos::fields::nc::enabled] = value::boolean(enabled); + data[nmos::fields::nc::members] = members; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager + web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + { + return make_nc_object(class_id, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, + const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, + const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause) + { + using web::json::value; + + auto data = details::make_nc_manager(nc_device_manager_class_id, oid, true, owner, U("DeviceManager"), user_label, touchpoints, runtime_property_constraints); + data[nmos::fields::nc::nc_version] = value::string(U("v1.0")); + data[nmos::fields::nc::manufacturer] = manufacturer; + data[nmos::fields::nc::product] = product; + data[nmos::fields::nc::serial_number] = value::string(serial_number); + data[nmos::fields::nc::user_inventory_code] = user_inventory_code; + data[nmos::fields::nc::device_name] = device_name; + data[nmos::fields::nc::device_role] = device_role; + data[nmos::fields::nc::operational_state] = operational_state; + data[nmos::fields::nc::reset_cause] = reset_cause; + data[nmos::fields::nc::message] = value::null(); + + return data; + } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label) { diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index bfecdd8e0..dc1d7673c 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -137,9 +137,6 @@ namespace nmos typedef std::map properties; -// typedef std::function method; -// typedef std::map methods; // method_id vs method handler - typedef std::function method; typedef std::map methods; // method_id vs method handler @@ -267,23 +264,6 @@ namespace nmos // constraints can be null web::json::value make_nc_datatype_typedef(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const utility::string_t& parent_type, bool is_sequence); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject - web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock - web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager - web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null()); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager - web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, - const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, - const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager(details::nc_oid oid, nc_oid owner, const web::json::value& user_label); - // make the core classes proprties/methods/events web::json::value make_nc_object_properties(); web::json::value make_nc_object_methods(); @@ -303,6 +283,23 @@ namespace nmos web::json::value make_nc_class_manager_properties(); web::json::value make_nc_class_manager_methods(); web::json::value make_nc_class_manager_events(); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject + web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager + web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null()); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, + const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, + const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + web::json::value make_nc_class_manager(details::nc_oid oid, nc_oid owner, const web::json::value& user_label); } } From bb873477e48442597b9bf96ded4b91df4ed3d3ee Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 8 Aug 2023 17:18:48 +0100 Subject: [PATCH 010/250] Update IS-12 schemas --- .../is-12/v1.0.x/APIs/schemas/command-message.json | 9 +++------ .../is-12/v1.0.x/APIs/schemas/notification-message.json | 9 +++------ .../v1.0.x/APIs/schemas/property-changed-event-data.json | 6 ++---- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/Development/third_party/is-12/v1.0.x/APIs/schemas/command-message.json b/Development/third_party/is-12/v1.0.x/APIs/schemas/command-message.json index cce540fa0..093b69eda 100644 --- a/Development/third_party/is-12/v1.0.x/APIs/schemas/command-message.json +++ b/Development/third_party/is-12/v1.0.x/APIs/schemas/command-message.json @@ -34,8 +34,7 @@ "oid": { "type": "integer", "description": "Object id containing the method", - "minimum": 1, - "maximum": 65535 + "minimum": 1 }, "methodId": { "type": "object", @@ -48,14 +47,12 @@ "level": { "type": "integer", "description": "Level component of the method ID", - "minimum": 0, - "maximum": 65535 + "minimum": 1 }, "index": { "type": "integer", "description": "Index component of the method ID", - "minimum": 1, - "maximum": 65535 + "minimum": 1 } } }, diff --git a/Development/third_party/is-12/v1.0.x/APIs/schemas/notification-message.json b/Development/third_party/is-12/v1.0.x/APIs/schemas/notification-message.json index c8a64e81d..860770eb4 100644 --- a/Development/third_party/is-12/v1.0.x/APIs/schemas/notification-message.json +++ b/Development/third_party/is-12/v1.0.x/APIs/schemas/notification-message.json @@ -28,8 +28,7 @@ "oid": { "type": "integer", "description": "Emitter object id", - "minimum": 1, - "maximum": 65535 + "minimum": 1 }, "eventId": { "type": "object", @@ -42,14 +41,12 @@ "level": { "type": "integer", "description": "Level component of the event ID", - "minimum": 0, - "maximum": 65535 + "minimum": 1 }, "index": { "type": "integer", "description": "Index component of the event ID", - "minimum": 1, - "maximum": 65535 + "minimum": 1 } } }, diff --git a/Development/third_party/is-12/v1.0.x/APIs/schemas/property-changed-event-data.json b/Development/third_party/is-12/v1.0.x/APIs/schemas/property-changed-event-data.json index 62249e306..7d6be6f1a 100644 --- a/Development/third_party/is-12/v1.0.x/APIs/schemas/property-changed-event-data.json +++ b/Development/third_party/is-12/v1.0.x/APIs/schemas/property-changed-event-data.json @@ -15,14 +15,12 @@ "level": { "type": "integer", "description": "Level component of the property ID", - "minimum": 0, - "maximum": 65535 + "minimum": 1 }, "index": { "type": "integer", "description": "Index component of the property ID", - "minimum": 1, - "maximum": 65535 + "minimum": 1 } } }, From 2aba14ac7a168c34e7a0478e6d3525288356dc74 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 8 Aug 2023 20:13:55 +0100 Subject: [PATCH 011/250] Tidy up make_nc_class_manager --- .../nmos/control_protocol_resource.cpp | 738 ++++++++++++------ 1 file changed, 494 insertions(+), 244 deletions(-) diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 3fddd49a4..b46dbcdf0 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -636,6 +636,453 @@ namespace nmos return value::array(); } + web::json::value make_nc_object_class() + { + using web::json::value; + + return make_nc_class_descriptor(value::string(U("NcObject class descriptor")), nc_object_class_id, U("NcObject"), value::null(), make_nc_object_properties(), make_nc_object_methods(), make_nc_object_events()); + } + + web::json::value make_nc_block_class() + { + using web::json::value; + + return make_nc_class_descriptor(value::string(U("NcBlock class descriptor")), nc_block_class_id, U("NcBlock"), value::null(), make_nc_block_properties(), make_nc_block_methods(), make_nc_block_events()); + } + + web::json::value make_nc_worker_class() + { + using web::json::value; + + return make_nc_class_descriptor(value::string(U("NcWorker class descriptor")), nc_worker_class_id, U("NcWorker"), value::null(), make_nc_worker_properties(), make_nc_worker_methods(), make_nc_worker_events()); + } + + web::json::value make_nc_manager_class() + { + using web::json::value; + + return make_nc_class_descriptor(value::string(U("NcManager class descriptor")), nc_manager_class_id, U("NcManager"), value::null(), make_nc_manager_properties(), make_nc_manager_methods(), make_nc_manager_events()); + } + + web::json::value make_nc_device_manager_class() + { + using web::json::value; + + return make_nc_class_descriptor(value::string(U("NcDeviceManager class descriptor")), nc_device_manager_class_id, U("NcDeviceManager"), value::string(U("DeviceManager")), make_nc_device_manager_properties(), make_nc_device_manager_methods(), make_nc_device_manager_events()); + } + + web::json::value make_nc_class_manager_class() + { + using web::json::value; + + return make_nc_class_descriptor(value::string(U("NcClassManager class descriptor")), nc_class_manager_class_id, U("NcClassManager"), value::string(U("ClassManager")), make_nc_class_manager_properties(), make_nc_class_manager_methods(), make_nc_class_manager_events()); + } + + web::json::value make_nc_class_id_datatype() + { + using web::json::value; + + return make_nc_datatype_typedef(value::string(U("Sequence of class ID fields")), U("NcClassId"), value::null(), U("NcInt32"), true); + } + + web::json::value make_nc_oid_datatype() + { + using web::json::value; + + return make_nc_datatype_typedef(value::string(U("Object id")), U("NcOid"), value::null(), U("NcUint32"), false); + } + + web::json::value make_nc_touchpoint_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Context namespace")), nmos::fields::nc::context_namespace, value::string(U("NcString")), false, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Base touchpoint class")), U("NcTouchpoint"), value::null(), fields, value::null()); + } + + web::json::value make_nc_element_id_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Level of the element")), nmos::fields::nc::level, value::string(U("NcUint16")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Index of the element")), nmos::fields::nc::index, value::string(U("NcUint16")), false, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Class element id which contains the level and index")), U("NcElementId"), value::null(), fields, value::null()); + } + + web::json::value make_nc_property_id_datatype() + { + using web::json::value; + + return make_nc_datatype_descriptor_struct(value::string(U("Property id which contains the level and index")), U("NcPropertyId"), value::null(), value::array(), value::string(U("NcElementId"))); + } + + web::json::value make_nc_property_contraints_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("The id of the property being constrained")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional default value")), nmos::fields::nc::default_value, value::null(), true, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Property constraints class")), U("NcPropertyConstraints"), value::null(), fields, value::null()); + } + + web::json::value make_nc_method_result_property_value_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Getter method value for the associated property")), nmos::fields::nc::value, value::null(), true, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Result when invoking the getter method associated with a property")), U("NcMethodResultPropertyValue"), value::null(), fields, value::string(U("NcMethodResult"))); + } + + web::json::value make_nc_method_status_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Method call was successful")), U("Ok"), 200)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Method call was successful but targeted property is deprecated")), U("PropertyDeprecated"), 298)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Method call was successful but method is deprecated")), U("MethodDeprecated"), 299)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Badly-formed command (e.g. the incoming command has invalid message encoding and cannot be parsed by the underlying protocol)")), U("BadCommandFormat"), 400)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Client is not authorized")), U("Unauthorized"), 401)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Command addresses a nonexistent object")), U("BadOid"), 404)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Attempt to change read-only state")), U("Readonly"), 405)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Method call is invalid in current operating context (e.g. attempting to invoke a method when the object is disabled)")), U("InvalidRequest"), 406)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("There is a conflict with the current state of the device")), U("Conflict"), 409)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Something was too big")), U("BufferOverflow"), 413)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Index is outside the available range")), U("IndexOutOfBounds"), 414)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Method parameter does not meet expectations (e.g. attempting to invoke a method with an invalid type for one of its parameters)")), U("ParameterError"), 417)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Addressed object is locked")), U("Locked"), 423)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Internal device error")), U("DeviceError"), 500)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Addressed method is not implemented by the addressed object")), U("MethodNotImplemented"), 501)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Addressed property is not implemented by the addressed object")), U("PropertyNotImplemented"), 502)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("The device is not ready to handle any commands")), U("NotReady"), 503)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Method call did not finish within the allotted time")), U("Timeout"), 504)); + return make_nc_datatype_descriptor_enum(value::string(U("Method invokation status")), U("NcMethodStatus"), value::null(), items); + } + + web::json::value make_nc_method_result_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Status for the invoked method")), nmos::fields::nc::status, value::string(U("NcMethodStatus")), false, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Base result of the invoked method")), U("NcMethodResult"), value::null(), fields, value::null()); + } + + web::json::value make_nc_id_datatype() + { + using web::json::value; + + return make_nc_datatype_typedef(value::string(U("Identity handler")), U("NcId"), value::null(), U("NcUint32"), false); + } + + web::json::value make_nc_method_result_id_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Id result value")), nmos::fields::nc::value, value::string(U("NcId")), false, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Id method result")), U("NcMethodResultId"), value::null(), fields, value::string(U("NcMethodResult"))); + } + + web::json::value make_method_result_length_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Length result value")), nmos::fields::nc::value, value::string(U("NcUint32")), true, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Length method result")), U("NcMethodResultLength"), value::null(), fields, value::string(U("NcMethodResult"))); + } + + web::json::value make_nc_property_change_type_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Current value changed")), U("ValueChanged"), 0)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Sequence item added")), U("SequenceItemAdded"), 1)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Sequence item changed")), U("SequenceItemChanged"), 2)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Sequence item removed")), U("SequenceItemRemoved"), 3)); + return make_nc_datatype_descriptor_enum(value::string(U("Type of property change")), U("NcPropertyChangeType"), value::null(), items); + } + + web::json::value make_nc_property_changed_event_data_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("The id of the property that changed")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Information regarding the change type")), nmos::fields::nc::change_type, value::string(U("NcPropertyChangeType")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Property-type specific value")), nmos::fields::nc::value, value::null(), true, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Index of sequence item if the property is a sequence")), nmos::fields::nc::sequence_item_index, value::string(U("NcId")), true, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Payload of property-changed event")), U("NcPropertyChangedEventData"), value::null(), fields, value::null()); + } + + web::json::value make_nc_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional user facing description")), nmos::fields::nc::description, value::string(U("NcString")), true, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Base descriptor")), U("NcDescriptor"), value::null(), fields, value::null()); + } + + web::json::value make_nc_block_member_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Role of member in its containing block")), nmos::fields::nc::role, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("OID of member")), nmos::fields::nc::oid, value::string(U("NcOid")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff member's OID is hardwired into device")), nmos::fields::nc::constant_oid, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Class ID")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("User label")), nmos::fields::nc::user_label, value::string(U("NcString")), true, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Containing block's OID")), nmos::fields::nc::owner, value::string(U("NcOid")), false, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Descriptor which is specific to a block member")), U("NcBlockMemberDescriptor"), value::null(), fields, value::string(U("NcDescriptor"))); + } + + web::json::value make_nc_method_result_block_member_descriptors_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Block member descriptors method result value")), nmos::fields::nc::value, value::string(U("NcBlockMemberDescriptor")), false, true, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Method result containing block member descriptors as the value")), U("NcMethodResultBlockMemberDescriptors"), value::null(), fields, value::string(U("NcMethodResult"))); + } + + web::json::value make_nc_version_code_datatype() + { + using web::json::value; + + return make_nc_datatype_typedef(value::string(U("Version code in semantic versioning format")), U("NcVersionCode"), value::null(), U("NcString"), false); + } + + web::json::value make_nc_organization_id_datatype() + { + using web::json::value; + + return make_nc_datatype_typedef(value::string(U("Unique 24-bit organization id")), U("NcOrganizationId"), value::null(), U("NcInt32"), false); + } + + web::json::value make_nc_uri_datatype() + { + using web::json::value; + + return make_nc_datatype_typedef(value::string(U("Uniform resource identifier")), U("NcUri"), value::null(), U("NcString"), false); + } + + web::json::value make_nc_manufacturer_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Manufacturer's name")), nmos::fields::nc::name, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("IEEE OUI or CID of manufacturer")), nmos::fields::nc::organization_id, value::string(U("NcOrganizationId")), true, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("URL of the manufacturer's website")), nmos::fields::nc::website, value::string(U("NcUri")), true, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Manufacturer descriptor")), U("NcManufacturer"), value::null(), fields, value::null()); + } + + web::json::value make_nc_uuid_datatype() + { + using web::json::value; + + return make_nc_datatype_typedef(value::string(U("UUID")), U("NcUuid"), value::null(), U("NcString"), false); + } + + web::json::value make_nc_product_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Product name")), nmos::fields::nc::name, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Manufacturer's unique key to product - model number, SKU, etc")), nmos::fields::nc::key, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Manufacturer's product revision level code")), nmos::fields::nc::revision_level, value::string(U("NcString")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Brand name under which product is sold")), nmos::fields::nc::brand_name, value::string(U("NcString")), true, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Unique UUID of product (not product instance)")), nmos::fields::nc::uuid, value::string(U("NcUuid")), true, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Text description of product")), nmos::fields::nc::description, value::string(U("NcString")), true, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Product descriptor")), U("NcProduct"), value::null(), fields, value::null()); + } + + web::json::value make_nc_device_generic_state_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Normal operation")), U("NormalOperation"), 1)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Device is initializing")), U("Initializing"), 2)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Device is performing a software or firmware update")), U("Updating"), 3)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Device is experiencing a licensing error")), U("LicensingError"), 4)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Device is experiencing an internal error")), U("InternalError"), 5)); + return make_nc_datatype_descriptor_enum(value::string(U("Device generic operational state")), U("NcDeviceGenericState"), value::null(), items); + } + + web::json::value make_nc_device_operational_state_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Generic operational state")), nmos::fields::nc::generic_state, value::string(U("NcDeviceGenericState")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Specific device details")), nmos::fields::nc::device_specific_details, value::string(U("NcString")), true, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Device operational state")), U("NcDeviceOperationalState"), value::null(), fields, value::null()); + } + + web::json::value make_nc_reset_cause_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Power on")), U("PowerOn"), 1)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Internal error")), U("InternalError"), 2)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Upgrade")), U("Upgrade"), 3)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Controller request")), U("ControllerRequest"), 4)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Manual request from the front panel")), U("ManualReset"), 5)); + return make_nc_datatype_descriptor_enum(value::string(U("Reset cause enum")), U("NcResetCause"), value::null(), items); + } + + web::json::value make_nc_name_datatype() + { + using web::json::value; + + return make_nc_datatype_typedef(value::string(U("Programmatically significant name, alphanumerics + underscore, no spaces")), U("NcName"), value::null(), U("NcString"), false); + } + + web::json::value make_nc_property_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Property id with level and index")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of property")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of property's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is read-only")), nmos::fields::nc::is_read_only, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class property")), U("NcPropertyDescriptor"), value::null(), fields, value::string(U("NcDescriptor"))); + } + + web::json::value make_nc_parameter_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of parameter")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of parameter's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a method parameter")), U("NcParameterDescriptor"), value::null(), fields, value::string(U("NcDescriptor"))); + } + + web::json::value make_nc_method_id_datatype() + { + using web::json::value; + + return make_nc_datatype_descriptor_struct(value::string(U("Method id which contains the level and index")), U("NcMethodId"), value::null(), value::array(), value::string(U("NcElementId"))); + } + + web::json::value make_nc_method_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Method id with level and index")), nmos::fields::nc::id, value::string(U("NcMethodId")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of method")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of method result's datatype")), nmos::fields::nc::result_datatype, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Parameter descriptors if any")), nmos::fields::nc::parameters, value::string(U("NcParameterDescriptor")), false, true, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class method")), U("NcMethodDescriptor"), value::null(), fields, value::string(U("NcDescriptor"))); + } + + web::json::value make_nc_event_id_datatype() + { + using web::json::value; + + return make_nc_datatype_descriptor_struct(value::string(U("Event id which contains the level and index")), U("NcEventId"), value::null(), value::array(), value::string(U("NcElementId"))); + } + + web::json::value make_nc_event_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Event id with level and index")), nmos::fields::nc::id, value::string(U("NcEventId")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of event")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of event data's datatype")), nmos::fields::nc::event_datatype, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class event")), U("NcEventDescriptor"), value::null(), fields, value::string(U("NcDescriptor"))); + } + + web::json::value make_nc_class_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Identity of the class")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of the class")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Role if the class has fixed role (manager classes)")), nmos::fields::nc::fixed_role, value::string(U("NcString")), true, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Property descriptors")), nmos::fields::nc::properties, value::string(U("NcPropertyDescriptor")), false, true, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Method descriptors")), nmos::fields::nc::methods, value::string(U("NcMethodDescriptor")), false, true, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Event descriptors")), nmos::fields::nc::events, value::string(U("NcEventDescriptor")), false, true, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class")), U("NcClassDescriptor"), value::null(), fields, value::string(U("NcDescriptor"))); + } + + web::json::value make_nc_parameter_constraints_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Default value")), nmos::fields::nc::default_value, value::null(), true, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Abstract parameter constraints class")), U("NcParameterConstraints"), value::null(), fields, value::null()); + } + + web::json::value make_nc_datatype_type_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Primitive datatype")), U("Primitive"), 0)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Simple alias of another datatype")), U("Typedef"), 1)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Data structure")), U("Struct"), 2)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Enum datatype")), U("Enum"), 3)); + return make_nc_datatype_descriptor_enum(value::string(U("Datatype type")), U("NcDatatypeType"), value::null(), items); + } + + web::json::value make_nc_datatype_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Datatype name")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Type: Primitive, Typedef, Struct, Enum")), nmos::fields::nc::type, value::string(U("NcDatatypeType")), false, false, value::null())); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Base datatype descriptor")), U("NcDatatypeDescriptor"), value::null(), fields, value::string(U("NcDescriptor"))); + } + + web::json::value make_nc_method_result_class_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Class descriptor method result value")), nmos::fields::nc::value, value::string(U("NcClassDescriptor")), false, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Method result containing a class descriptor as the value")), U("NcMethodResultClassDescriptor"), value::null(), fields, value::string(U("NcMethodResult"))); + } + + web::json::value make_nc_method_result_datatype_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Datatype descriptor method result value")), nmos::fields::nc::value, value::string(U("NcDatatypeDescriptor")), false, false, value::null())); + return make_nc_datatype_descriptor_struct(value::string(U("Method result containing a datatype descriptor as the value")), U("NcMethodResultDatatypeDescriptor"), value::null(), fields, value::string(U("NcMethodResult"))); + } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { @@ -704,149 +1151,64 @@ namespace nmos auto data = make_nc_manager(nc_class_manager_class_id, oid, true, owner, U("ClassManager"), user_label); - // load the minimal control classes + // minimal control classes data[nmos::fields::nc::control_classes] = value::array(); auto& control_classes = data[nmos::fields::nc::control_classes]; // NcObject control class - web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcObject class descriptor")), nc_object_class_id, U("NcObject"), value::null(), make_nc_object_properties(), make_nc_object_methods(), make_nc_object_events())); + web::json::push_back(control_classes, make_nc_object_class()); // NcBlock control class - web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcBlock class descriptor")), nc_block_class_id, U("NcBlock"), value::null(), make_nc_block_properties(), make_nc_block_methods(), make_nc_block_events())); + web::json::push_back(control_classes, make_nc_block_class()); // NcWorker control class - web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcWorker class descriptor")), nc_worker_class_id, U("NcWorker"), value::null(), make_nc_worker_properties(), make_nc_worker_methods(), make_nc_worker_events())); + web::json::push_back(control_classes, make_nc_worker_class()); // NcManager control class - web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcManager class descriptor")), nc_manager_class_id, U("NcManager"), value::null(), make_nc_manager_properties(), make_nc_manager_methods(), make_nc_manager_events())); + web::json::push_back(control_classes, make_nc_manager_class()); // NcDeviceManager control class - web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcDeviceManager class descriptor")), nc_device_manager_class_id, U("NcDeviceManager"), value::string(U("DeviceManager")), make_nc_device_manager_properties(), make_nc_device_manager_methods(), make_nc_device_manager_events())); + web::json::push_back(control_classes, make_nc_device_manager_class()); // NcClassManager control class - web::json::push_back(control_classes, details::make_nc_class_descriptor(value::string(U("NcClassManager class descriptor")), nc_class_manager_class_id, U("NcClassManager"), value::string(U("ClassManager")), make_nc_class_manager_properties(), make_nc_class_manager_methods(), make_nc_class_manager_events())); + web::json::push_back(control_classes, make_nc_class_manager_class()); - // load the minimal datatypes + // minimal datatypes data[nmos::fields::nc::datatypes] = value::array(); auto& datatypes = data[nmos::fields::nc::datatypes]; // NcObject datatypes // NcClassId - web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Sequence of class ID fields")), U("NcClassId"), value::null(), U("NcInt32"), true)); + web::json::push_back(datatypes, make_nc_class_id_datatype()); // NcOid - web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Object id")), U("NcOid"), value::null(), U("NcUint32"), false)); + web::json::push_back(datatypes, make_nc_oid_datatype()); // NcTouchpoint - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Context namespace")), nmos::fields::nc::context_namespace, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Base touchpoint class")), U("NcTouchpoint"), value::null(), fields, value::null())); - } + web::json::push_back(datatypes, make_nc_touchpoint_datatype()); // NcElementId - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Level of the element")), nmos::fields::nc::level, value::string(U("NcUint16")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Index of the element")), nmos::fields::nc::index, value::string(U("NcUint16")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Class element id which contains the level and index")), U("NcElementId"), value::null(), fields, value::null())); - } + web::json::push_back(datatypes, make_nc_element_id_datatype()); // NcPropertyId - { - auto fields = value::array(); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Property id which contains the level and index")), U("NcPropertyId"), value::null(), fields, value::string(U("NcElementId")))); - } + web::json::push_back(datatypes, make_nc_property_id_datatype()); // NcPropertyConstraints - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("The id of the property being constrained")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional default value")), nmos::fields::nc::default_value, value::null(), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Property constraints class")), U("NcPropertyConstraints"), value::null(), fields, value::null())); - } + web::json::push_back(datatypes, make_nc_property_contraints_datatype()); // NcMethodResultPropertyValue - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Getter method value for the associated property")), nmos::fields::nc::value, value::null(), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Result when invoking the getter method associated with a property")), U("NcMethodResultPropertyValue"), value::null(), fields, value::string(U("NcMethodResult")))); - } + web::json::push_back(datatypes, make_nc_method_result_property_value_datatype()); // NcMethodStatus - { - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful")), U("Ok"), 200)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful but targeted property is deprecated")), U("PropertyDeprecated"), 298)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful but method is deprecated")), U("MethodDeprecated"), 299)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Badly-formed command (e.g. the incoming command has invalid message encoding and cannot be parsed by the underlying protocol)")), U("BadCommandFormat"), 400)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Client is not authorized")), U("Unauthorized"), 401)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Command addresses a nonexistent object")), U("BadOid"), 404)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Attempt to change read-only state")), U("Readonly"), 405)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call is invalid in current operating context (e.g. attempting to invoke a method when the object is disabled)")), U("InvalidRequest"), 406)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("There is a conflict with the current state of the device")), U("Conflict"), 409)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Something was too big")), U("BufferOverflow"), 413)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Index is outside the available range")), U("IndexOutOfBounds"), 414)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method parameter does not meet expectations (e.g. attempting to invoke a method with an invalid type for one of its parameters)")), U("ParameterError"), 417)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed object is locked")), U("Locked"), 423)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Internal device error")), U("DeviceError"), 500)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed method is not implemented by the addressed object")), U("MethodNotImplemented"), 501)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed property is not implemented by the addressed object")), U("PropertyNotImplemented"), 502)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("The device is not ready to handle any commands")), U("NotReady"), 503)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call did not finish within the allotted time")), U("Timeout"), 504)); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Method invokation status")), U("NcMethodStatus"), value::null(), items)); - } + web::json::push_back(datatypes, make_nc_method_status_datatype()); // NcMethodResult - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Status for the invoked method")), nmos::fields::nc::status, value::string(U("NcMethodStatus")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Base result of the invoked method")), U("NcMethodResult"), value::null(), fields, value::null())); - } + web::json::push_back(datatypes, make_nc_method_result_datatype()); // NcId - web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Identity handler")), U("NcId"), value::null(), U("NcUint32"), false)); + web::json::push_back(datatypes, make_nc_id_datatype()); // NcMethodResultId - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Id result value")), nmos::fields::nc::value, value::string(U("NcId")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Id method result")), U("NcMethodResultId"), value::null(), fields, value::string(U("NcMethodResult")))); - } + web::json::push_back(datatypes, make_nc_method_result_id_datatype()); // NcMethodResultLength - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Length result value")), nmos::fields::nc::value, value::string(U("NcUint32")), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Length method result")), U("NcMethodResultLength"), value::null(), fields, value::string(U("NcMethodResult")))); - } + web::json::push_back(datatypes, make_method_result_length_datatype()); // NcPropertyChangeType - { - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Current value changed")), U("ValueChanged"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item added")), U("SequenceItemAdded"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item changed")), U("SequenceItemChanged"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item removed")), U("SequenceItemRemoved"), 3)); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Type of property change")), U("NcPropertyChangeType"), value::null(), items)); - } + web::json::push_back(datatypes, make_nc_property_change_type_datatype()); // NcPropertyChangedEventData - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("The id of the property that changed")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Information regarding the change type")), nmos::fields::nc::change_type, value::string(U("NcPropertyChangeType")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property-type specific value")), nmos::fields::nc::value, value::null(), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Index of sequence item if the property is a sequence")), nmos::fields::nc::sequence_item_index, value::string(U("NcId")), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Payload of property-changed event")), U("NcPropertyChangedEventData"), value::null(), fields, value::null())); - } + web::json::push_back(datatypes, make_nc_property_changed_event_data_datatype()); // NcBlock datatypes // NcDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional user facing description")), nmos::fields::nc::description, value::string(U("NcString")), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Base descriptor")), U("NcDescriptor"), value::null(), fields, value::null())); - } + web::json::push_back(datatypes, make_nc_descriptor_datatype()); // NcBlockMemberDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Role of member in its containing block")), nmos::fields::nc::role, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("OID of member")), nmos::fields::nc::oid, value::string(U("NcOid")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff member's OID is hardwired into device")), nmos::fields::nc::constant_oid, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Class ID")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("User label")), nmos::fields::nc::user_label, value::string(U("NcString")), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Containing block's OID")), nmos::fields::nc::owner, value::string(U("NcOid")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor which is specific to a block member")), U("NcBlockMemberDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); - } + web::json::push_back(datatypes, make_nc_block_member_descriptor_datatype()); // NcMethodResultBlockMemberDescriptors - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Block member descriptors method result value")), nmos::fields::nc::value, value::string(U("NcBlockMemberDescriptor")), false, true, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing block member descriptors as the value")), U("NcMethodResultBlockMemberDescriptors"), value::null(), fields, value::string(U("NcMethodResult")))); - } + web::json::push_back(datatypes, make_nc_method_result_block_member_descriptors_datatype()); // NcWorker has no datatypes @@ -854,163 +1216,51 @@ namespace nmos // NcDeviceManager datatypes // NcVersionCode - web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Version code in semantic versioning format")), U("NcVersionCode"), value::null(), U("NcString"), false)); + web::json::push_back(datatypes, make_nc_version_code_datatype()); // NcOrganizationId - web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Unique 24-bit organization id")), U("NcOrganizationId"), value::null(), U("NcInt32"), false)); + web::json::push_back(datatypes, make_nc_organization_id_datatype()); // NcUri - web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Uniform resource identifier")), U("NcUri"), value::null(), U("NcString"), false)); + web::json::push_back(datatypes, make_nc_uri_datatype()); // NcManufacturer - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's name")), nmos::fields::nc::name, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("IEEE OUI or CID of manufacturer")), nmos::fields::nc::organization_id, value::string(U("NcOrganizationId")), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("URL of the manufacturer's website")), nmos::fields::nc::website, value::string(U("NcUri")), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Manufacturer descriptor")), U("NcManufacturer"), value::null(), fields, value::null())); - } + web::json::push_back(datatypes, make_nc_manufacturer_datatype()); // NcUuid - web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("UUID")), U("NcUuid"), value::null(), U("NcString"), false)); + web::json::push_back(datatypes, make_nc_uuid_datatype()); // NcProduct - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Product name")), nmos::fields::nc::name, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's unique key to product - model number, SKU, etc")), nmos::fields::nc::key, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's product revision level code")), nmos::fields::nc::revision_level, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Brand name under which product is sold")), nmos::fields::nc::brand_name, value::string(U("NcString")), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Unique UUID of product (not product instance)")), nmos::fields::nc::uuid, value::string(U("NcUuid")), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Text description of product")), nmos::fields::nc::description, value::string(U("NcString")), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Product descriptor")), U("NcProduct"), value::null(), fields, value::null())); - } + web::json::push_back(datatypes, make_nc_product_datatype()); // NcDeviceGenericState - { - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Normal operation")), U("NormalOperation"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is initializing")), U("Initializing"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is performing a software or firmware update")), U("Updating"), 3)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is experiencing a licensing error")), U("LicensingError"), 4)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is experiencing an internal error")), U("InternalError"), 5)); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Device generic operational state")), U("NcDeviceGenericState"), value::null(), items)); - } + web::json::push_back(datatypes, make_nc_device_generic_state_datatype()); // NcDeviceOperationalState - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Generic operational state")), nmos::fields::nc::generic_state, value::string(U("NcDeviceGenericState")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Specific device details")), nmos::fields::nc::device_specific_details, value::string(U("NcString")), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Device operational state")), U("NcDeviceOperationalState"), value::null(), fields, value::null())); - } + web::json::push_back(datatypes, make_nc_device_operational_state_datatype()); // NcResetCause - { - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Power on")), U("PowerOn"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Internal error")), U("InternalError"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Upgrade")), U("Upgrade"), 3)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Controller request")), U("ControllerRequest"), 4)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Manual request from the front panel")), U("ManualReset"), 5)); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Reset cause enum")), U("NcResetCause"), value::null(), items)); - } + web::json::push_back(datatypes, make_nc_reset_cause_datatype()); // NcClassManager datatypes // NcName - web::json::push_back(datatypes, details::make_nc_datatype_typedef(value::string(U("Programmatically significant name, alphanumerics + underscore, no spaces")), U("NcName"), value::null(), U("NcString"), false)); + web::json::push_back(datatypes, make_nc_name_datatype()); // NcPropertyDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property id with level and index")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of property")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of property's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is read-only")), nmos::fields::nc::is_read_only, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class property")), U("NcPropertyDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); - } + web::json::push_back(datatypes, make_nc_property_descriptor_datatype()); // NcMethodId - { - auto fields = value::array(); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Method id which contains the level and index")), U("NcMethodId"), value::null(), fields, value::string(U("NcElementId")))); - } + web::json::push_back(datatypes, make_nc_method_id_datatype()); // NcParameterDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of parameter")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of parameter's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a method parameter")), U("NcParameterDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); - } + web::json::push_back(datatypes, make_nc_parameter_descriptor_datatype()); // NcMethodDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Method id with level and index")), nmos::fields::nc::id, value::string(U("NcMethodId")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of method")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of method result's datatype")), nmos::fields::nc::result_datatype, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Parameter descriptors if any")), nmos::fields::nc::parameters, value::string(U("NcParameterDescriptor")), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class method")), U("NcMethodDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); - } + web::json::push_back(datatypes, make_nc_method_descriptor_datatype()); // NcEventId - { - auto fields = value::array(); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Event id which contains the level and index")), U("NcEventId"), value::null(), fields, value::string(U("NcElementId")))); - } + web::json::push_back(datatypes, make_nc_event_id_datatype()); // NcEventDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Event id with level and index")), nmos::fields::nc::id, value::string(U("NcEventId")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of event")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of event data's datatype")), nmos::fields::nc::event_datatype, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class event")), U("NcEventDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); - } + web::json::push_back(datatypes, make_nc_event_descriptor_datatype()); // NcClassDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Identity of the class")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of the class")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Role if the class has fixed role (manager classes)")), nmos::fields::nc::fixed_role, value::string(U("NcString")), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property descriptors")), nmos::fields::nc::properties, value::string(U("NcPropertyDescriptor")), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Method descriptors")), nmos::fields::nc::methods, value::string(U("NcMethodDescriptor")), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Event descriptors")), nmos::fields::nc::events, value::string(U("NcEventDescriptor")), false, true, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class")), U("NcClassDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); - } + web::json::push_back(datatypes, make_nc_class_descriptor_datatype()); // NcParameterConstraints - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Default value")), nmos::fields::nc::default_value, value::null(), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Abstract parameter constraints class")), U("NcParameterConstraints"), value::null(), fields, value::null())); - } + web::json::push_back(datatypes, make_nc_parameter_constraints_datatype()); // NcDatatypeType - { - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Primitive datatype")), U("Primitive"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Simple alias of another datatype")), U("Typedef"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Data structure")), U("Struct"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Enum datatype")), U("Enum"), 3)); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_enum(value::string(U("Datatype type")), U("NcDatatypeType"), value::null(), items)); - } + web::json::push_back(datatypes, make_nc_datatype_type_datatype()); // NcDatatypeDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Datatype name")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Type: Primitive, Typedef, Struct, Enum")), nmos::fields::nc::type, value::string(U("NcDatatypeType")), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Base datatype descriptor")), U("NcDatatypeDescriptor"), value::null(), fields, value::string(U("NcDescriptor")))); - } + web::json::push_back(datatypes, make_nc_datatype_descriptor_datatype()); // NcMethodResultClassDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Class descriptor method result value")), nmos::fields::nc::value, value::string(U("NcClassDescriptor")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing a class descriptor as the value")), U("NcMethodResultClassDescriptor"), value::null(), fields, value::string(U("NcMethodResult")))); - } + web::json::push_back(datatypes, make_nc_method_result_class_descriptor_datatype()); // NcMethodResultDatatypeDescriptor - { - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Datatype descriptor method result value")), nmos::fields::nc::value, value::string(U("NcDatatypeDescriptor")), false, false, value::null())); - web::json::push_back(datatypes, details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing a datatype descriptor as the value")), U("NcMethodResultDatatypeDescriptor"), value::null(), fields, value::string(U("NcMethodResult")))); - } + web::json::push_back(datatypes, make_nc_method_result_datatype_descriptor_datatype()); return data; } From 71f97b5b22ac371df09750cce8083345e246a074 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 8 Aug 2023 21:25:59 +0100 Subject: [PATCH 012/250] Add NcObject's GetSequenceItem --- Development/nmos/control_protocol_ws_api.cpp | 78 +++++++++++++++++--- 1 file changed, 66 insertions(+), 12 deletions(-) diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 6639bcc93..a8a6965ed 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -54,7 +54,7 @@ namespace nmos // hmm, methods should also be passing in via the control_class::methods // NcObject methods implementation - // get property + // Get property auto get = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -79,16 +79,16 @@ namespace nmos // unknown property utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << " to do get"; + ss << U("unknown property: ") << property_id.serialize() << " to do Get"; return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); } // resource not found for the given oid utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to do get"; + ss << U("unknown oid: ") << oid << " to do Get"; return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; - // set property + // Set property auto set = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -125,13 +125,67 @@ namespace nmos // unknown property utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << " to do set"; + ss << U("unknown property: ") << property_id.serialize() << " to do Set"; return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); } // resource not found for the given oid utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to do set"; + ss << U("unknown oid: ") << oid << " to do Set"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + }; + // GetSequenceItem + auto get_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto& resources = model.control_protocol_resources; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& index = nmos::fields::nc::index(arguments); + + // find the relevant nc_property_descriptor + auto property_found = std::find_if(properties.begin(), properties.end(), [property_id](const web::json::value& property) + { + return property_id == nmos::fields::nc::id(property); + }); + + if (property_found != properties.end()) + { + if (nmos::fields::nc::is_sequence(*property_found)) + { + auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); + + if (!data.is_null() && data.as_array().size() > index) + { + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, data.at(index)); + } + + // out of bound + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << " is outside the available range to do GetSequenceItem"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } + else + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << " is not a sequence to do GetSequenceItem"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << " to do GetSequenceItem"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + } + + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid << " to do get"; return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; @@ -181,30 +235,30 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 1 } })] = get; nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 2 } })] = set; - //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 3 } })] = get_sequence_item; + nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 3 } })] = get_sequence_item; //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 4 } })] = set_sequence_item; //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 5 } })] = add_sequence_item; //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 6 } })] = remove_sequence_item; //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 7 } })] = get_sequence_length; // NcBlock methods - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 1 } })] = get_member_descriptors; //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 2 } })] = find_members_by_path; //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 3 } })] = find_members_by_role; //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 4 } })] = find_members_by_class_id; // NcWorker has no extended method - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker // NcManager has no extended method - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager // NcDeviceManger has no extended method - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager // NcClassManager methods - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 1 } })] = get_control_class; //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 2 } })] = get_datatype; From 6e1640f002602570774473af6b507c0d0f9d9561 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 8 Aug 2023 22:56:28 +0100 Subject: [PATCH 013/250] Fix GetSequenceItem and add GetSequenceLength --- Development/nmos/control_protocol_resource.h | 1 + Development/nmos/control_protocol_ws_api.cpp | 78 ++++++++++++++++++-- 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index dc1d7673c..a3a13b8cd 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -44,6 +44,7 @@ namespace nmos invalid_request = 406, // Method call is invalid in current operating context conflict = 409, // There is a conflict with the current state of the device buffer_overflow = 413, // Something was too big + index_out_of_bounds = 414, // Index is outside the available range parameter_error = 417, // Method parameter does not meet expectations locked = 423, // Addressed object is locked device_error = 500, // Internal device error diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index a8a6965ed..036030bd9 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -54,7 +54,7 @@ namespace nmos // hmm, methods should also be passing in via the control_class::methods // NcObject methods implementation - // Get property + // Get property value auto get = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -88,7 +88,7 @@ namespace nmos ss << U("unknown oid: ") << oid << " to do Get"; return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; - // Set property + // Set property value auto set = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -134,7 +134,7 @@ namespace nmos ss << U("unknown oid: ") << oid << " to do Set"; return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; - // GetSequenceItem + // Get sequence item auto get_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -166,7 +166,7 @@ namespace nmos // out of bound utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << " is outside the available range to do GetSequenceItem"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); } else { @@ -188,9 +188,74 @@ namespace nmos ss << U("unknown oid: ") << oid << " to do get"; return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; + // Get sequence length + auto get_sequence_length = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto& resources = model.control_protocol_resources; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + const auto& property_id = nmos::fields::nc::id(arguments); + + // find the relevant nc_property_descriptor + auto property_found = std::find_if(properties.begin(), properties.end(), [property_id](const web::json::value& property) + { + return property_id == nmos::fields::nc::id(property); + }); + + if (property_found != properties.end()) + { + if (nmos::fields::nc::is_sequence(*property_found)) + { + auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); + + if (nmos::fields::nc::is_nullable(*property_found)) + { + // can be null + if (data.is_null()) + { + // null + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, value::null()); + } + } + else + { + // cannot be null + if (data.is_null()) + { + // null + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << " is a null sequence to do GetSequenceLength"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } + } + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, data.as_array().size()); + } + else + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << " is not a sequence to do GetSequenceLength"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << " to do GetSequenceItem"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + } + + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid << " to do get"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + }; // NcBlock methods implementation - // get descriptors of members of the block + // Gets descriptors of members of the block auto get_member_descriptors = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -213,6 +278,7 @@ namespace nmos }; // NcClassManager methods implementation + // Get a single class descriptor auto get_control_class = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) { // hmm, todo @@ -239,7 +305,7 @@ namespace nmos //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 4 } })] = set_sequence_item; //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 5 } })] = add_sequence_item; //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 6 } })] = remove_sequence_item; - //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 7 } })] = get_sequence_length; + nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 7 } })] = get_sequence_length; // NcBlock methods // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock From a4455bc0be716fbc7ed2af790706a9b9dd4f1e98 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 9 Aug 2023 17:42:15 +0100 Subject: [PATCH 014/250] Code tidy up --- Development/nmos/control_protocol_resource.cpp | 16 ++++++++-------- Development/nmos/control_protocol_resource.h | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index b46dbcdf0..3377f1682 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -7,7 +7,7 @@ namespace nmos { namespace details { - web::json::value make_control_protocol_result(const nc_method_result& method_result) + web::json::value make_nc_method_result(const nc_method_result& method_result) { using web::json::value_of; @@ -16,16 +16,16 @@ namespace nmos }); } - web::json::value make_control_protocol_error_result(const nc_method_result& method_result, const utility::string_t& error_message) + web::json::value make_nc_method_result_error(const nc_method_result& method_result, const utility::string_t& error_message) { - auto result = make_control_protocol_result(method_result); + auto result = make_nc_method_result(method_result); if (!error_message.empty()) { result[nmos::fields::nc::error_message] = web::json::value::string(error_message); } return result; } - web::json::value make_control_protocol_result(const nc_method_result& method_result, const web::json::value& value) + web::json::value make_nc_method_result(const nc_method_result& method_result, const web::json::value& value) { - auto result = make_control_protocol_result(method_result); + auto result = make_nc_method_result(method_result); result[nmos::fields::nc::value] = value; return result; } @@ -36,7 +36,7 @@ namespace nmos return value_of({ { nmos::fields::nc::handle, handle }, - { nmos::fields::nc::result, make_control_protocol_error_result(method_result, error_message) } + { nmos::fields::nc::result, make_nc_method_result_error(method_result, error_message) } }); } @@ -46,7 +46,7 @@ namespace nmos return value_of({ { nmos::fields::nc::handle, handle }, - { nmos::fields::nc::result, make_control_protocol_result(method_result) } + { nmos::fields::nc::result, make_nc_method_result(method_result) } }); } @@ -56,7 +56,7 @@ namespace nmos return value_of({ { nmos::fields::nc::handle, handle }, - { nmos::fields::nc::result, make_control_protocol_result(method_result, value) } + { nmos::fields::nc::result, make_nc_method_result(method_result, value) } }); } diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index a3a13b8cd..458178d7b 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -141,14 +141,14 @@ namespace nmos typedef std::function method; typedef std::map methods; // method_id vs method handler - web::json::value make_control_protocol_result(const nc_method_result& method_result); - web::json::value make_control_protocol_error_result(const nc_method_result& method_result, const utility::string_t& error_message); - - web::json::value make_control_protocol_result(const nc_method_result& method_result, const web::json::value& value); web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message); web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result); + // value can be + // sequence + // NcClassDescriptor + // NcDatatypeDescriptor web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value); // message response From bbf260496a88cb003d2a8c826a8111c340fa76bd Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 9 Aug 2023 17:44:20 +0100 Subject: [PATCH 015/250] Add SetSequenceItem, AddSequenceItem, RemoveSequenceItem, FindMembersByPath --- Development/nmos/control_protocol_ws_api.cpp | 293 ++++++++++++++++--- 1 file changed, 253 insertions(+), 40 deletions(-) diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 036030bd9..6b0d6d157 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -55,7 +55,7 @@ namespace nmos // NcObject methods implementation // Get property value - auto get = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + const auto get = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -89,7 +89,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Set property value - auto set = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + const auto set = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -107,20 +107,24 @@ namespace nmos }); if (property_found != properties.end()) { - if (!nmos::fields::nc::is_read_only(*property_found)) + if (nmos::fields::nc::is_read_only(*property_found)) { - resources.modify(resource, [&](nmos::resource& resource) - { - resource.data[nmos::fields::nc::name(*property_found)] = val; - - resource.updated = strictly_increasing_update(resources); - }); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); + return details::make_control_protocol_response(handle, { details::nc_method_status::read_only }); } - else + + if ((val.is_null() && !nmos::fields::nc::is_nullable(*property_found)) + || (val.is_array() && !nmos::fields::nc::is_sequence(*property_found))) { - return details::make_control_protocol_response(handle, { details::nc_method_status::read_only }); + return details::make_control_protocol_response(handle, { details::nc_method_status::parameter_error }); } + + resources.modify(resource, [&](nmos::resource& resource) + { + resource.data[nmos::fields::nc::name(*property_found)] = val; + + resource.updated = strictly_increasing_update(resources); + }); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); } // unknown property @@ -135,7 +139,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Get sequence item - auto get_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + const auto get_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -154,42 +158,187 @@ namespace nmos if (property_found != properties.end()) { - if (nmos::fields::nc::is_sequence(*property_found)) - { - auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); - - if (!data.is_null() && data.as_array().size() > index) - { - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, data.at(index)); - } - - // out of bound - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << " is outside the available range to do GetSequenceItem"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); - } - else + if (!nmos::fields::nc::is_sequence(*property_found)) { // property is not a sequence utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << " is not a sequence to do GetSequenceItem"; return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); } + + auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); + + if (!data.is_null() && data.as_array().size() > index) + { + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, data.at(index)); + } + + // out of bound + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << " is outside the available range to do GetSequenceItem"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); } // unknown property utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << " to do GetSequenceItem"; + ss << U("unknown property: ") << property_id.serialize( + ) << " to do GetSequenceItem"; return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); } // resource not found for the given oid utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to do get"; + ss << U("unknown oid: ") << oid << " to do GetSequenceItem"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + }; + // Set sequence item + const auto set_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto& resources = model.control_protocol_resources; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& index = nmos::fields::nc::index(arguments); + const auto& val = nmos::fields::nc::value(arguments); + + // find the relevant nc_property_descriptor + auto property_found = std::find_if(properties.begin(), properties.end(), [property_id](const web::json::value& property) + { + return property_id == nmos::fields::nc::id(property); + }); + + if (!nmos::fields::nc::is_sequence(*property_found)) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << " is not a sequence to do SetSequenceItem"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } + + auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); + + if (!data.is_null() && data.as_array().size() > index) + { + resources.modify(resource, [&](nmos::resource& resource) + { + resource.data[nmos::fields::nc::name(*property_found)][index] = val; + + resource.updated = strictly_increasing_update(resources); + }); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); + } + + // out of bound + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << " is outside the available range to do SetSequenceItem"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); + } + + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid << " to do SetSequenceItem"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + }; + // Add item to sequence + const auto add_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto& resources = model.control_protocol_resources; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& val = nmos::fields::nc::value(arguments); + + // find the relevant nc_property_descriptor + auto property_found = std::find_if(properties.begin(), properties.end(), [property_id](const web::json::value& property) + { + return property_id == nmos::fields::nc::id(property); + }); + + if (!nmos::fields::nc::is_sequence(*property_found)) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << " is not a sequence to do AddSequenceItem"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } + + auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); + + resources.modify(resource, [&](nmos::resource& resource) + { + auto& sequence = resource.data[nmos::fields::nc::name(*property_found)]; + if (data.is_null()) { sequence = value::array(); } + web::json::push_back(sequence, val); + + resource.updated = strictly_increasing_update(resources); + }); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, data.as_array().size() - 1); + } + + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid << " to do AddSequenceItem"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + }; + // Delete sequence item + const auto remove_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto& resources = model.control_protocol_resources; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& index = nmos::fields::nc::index(arguments); + + // find the relevant nc_property_descriptor + auto property_found = std::find_if(properties.begin(), properties.end(), [property_id](const web::json::value& property) + { + return property_id == nmos::fields::nc::id(property); + }); + + if (!nmos::fields::nc::is_sequence(*property_found)) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << " is not a sequence to do RemoveSequenceItem"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } + + auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); + + if (!data.is_null() && data.as_array().size() > index) + { + resources.modify(resource, [&](nmos::resource& resource) + { + auto& sequence = resource.data[nmos::fields::nc::name(*property_found)].as_array(); + sequence.erase(index); + + resource.updated = strictly_increasing_update(resources); + }); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); + } + + // out of bound + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << " is outside the available range to do RemoveSequenceItem"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); + } + + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid << " to do RemoveSequenceItem"; return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Get sequence length - auto get_sequence_length = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + const auto get_sequence_length = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -244,19 +393,19 @@ namespace nmos // unknown property utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << " to do GetSequenceItem"; + ss << U("unknown property: ") << property_id.serialize() << " to do GetSequenceLength"; return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); } // resource not found for the given oid utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to do get"; + ss << U("unknown oid: ") << oid << " to do GetSequenceLength"; return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // NcBlock methods implementation // Gets descriptors of members of the block - auto get_member_descriptors = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments) + const auto get_member_descriptors = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -273,13 +422,77 @@ namespace nmos // resource not found for the given oid utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to get member descriptors"; + ss << U("unknown oid: ") << oid << " to do GetMemberDescriptors"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + }; + // Finds member(s) by path + const auto find_members_by_path = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto& resources = model.control_protocol_resources; + + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + // Relative path to search for (MUST not include the role of the block targeted by oid) + const auto& path = nmos::fields::nc::path(arguments); + + if (0 == path.size()) + { + // empty path + return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty path to do FindMembersByPath")); + } + + value nc_block_member_descriptor; + + for (const auto& role : path) + { + // look for the role in members + if (resource->data.has_field(nmos::fields::nc::members)) + { + auto& members = nmos::fields::nc::members(resource->data); + auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& nc_block_member_descriptor) + { + return role.as_string() == nmos::fields::nc::role(nc_block_member_descriptor); + }); + + if (members.end() != member_found) + { + nc_block_member_descriptor = *member_found; + + // use oid to look for next resource + resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(*member_found)))); + } + else + { + // should + // no role + utility::stringstream_t ss; + ss << U("role: ") << role.as_string() << U(" not found to do FindMembersByPath"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + } + } + else + { + // should + // no members + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, U("no members to do FindMembersByPath")); + } + } + + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, nc_block_member_descriptor); + } + + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid << " to do FindMembersByPath"; return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // NcClassManager methods implementation // Get a single class descriptor - auto get_control_class = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + const auto get_control_class = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) { // hmm, todo @@ -302,15 +515,15 @@ namespace nmos nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 1 } })] = get; nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 2 } })] = set; nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 3 } })] = get_sequence_item; - //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 4 } })] = set_sequence_item; - //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 5 } })] = add_sequence_item; - //nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 6 } })] = remove_sequence_item; + nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 4 } })] = set_sequence_item; + nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 5 } })] = add_sequence_item; + nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 6 } })] = remove_sequence_item; nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 7 } })] = get_sequence_length; // NcBlock methods // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 1 } })] = get_member_descriptors; - //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 2 } })] = find_members_by_path; + nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 2 } })] = find_members_by_path; //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 3 } })] = find_members_by_role; //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 4 } })] = find_members_by_class_id; From b5cbfc765adaac460ab216115252ab1246da681f Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 9 Aug 2023 22:23:18 +0100 Subject: [PATCH 016/250] Fix AddSequenceItem --- Development/nmos/control_protocol_resource.cpp | 7 +++++++ Development/nmos/control_protocol_resource.h | 1 + Development/nmos/control_protocol_ws_api.cpp | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 3377f1682..974c51aba 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -60,6 +60,13 @@ namespace nmos }); } + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, uint32_t value_) + { + using web::json::value; + + return make_control_protocol_response(handle, method_result, value(value_)); + } + // message response // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-message-type web::json::value make_control_protocol_message_response(nc_message_type::type type, const web::json::value& responses) diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 458178d7b..dca604afa 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -66,6 +66,7 @@ namespace nmos web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message); web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result); web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value); + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, uint32_t value); // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-message-type web::json::value make_control_protocol_message_response(nc_message_type::type type, const web::json::value& responses); diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 6b0d6d157..30ff1e4c7 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -278,7 +278,7 @@ namespace nmos resource.updated = strictly_increasing_update(resources); }); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, data.as_array().size() - 1); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, uint32_t(data.as_array().size() - 1)); } // resource not found for the given oid From 4eab0e0e59a5a58f9214a655893d751aa10eaf22 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 10 Aug 2023 11:04:16 +0100 Subject: [PATCH 017/250] Fix FindMembersByPath --- Development/nmos/control_protocol_ws_api.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 30ff1e4c7..a92e73fc9 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -1,6 +1,8 @@ #include "nmos/control_protocol_ws_api.h" +#include #include +#include "bst/regex.h" #include "cpprest/json_validator.h" #include "cpprest/regex_utils.h" #include "nmos/api_utils.h" @@ -380,7 +382,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); } } - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, data.as_array().size()); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, uint32_t(data.as_array().size())); } else { @@ -417,6 +419,7 @@ namespace nmos // hmm, If recurse is set to true, nested members is to be retrieved const auto& recurse = nmos::fields::nc::recurse(arguments); + // return the descriptors of members of the block return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, resource->data.at(nmos::fields::nc::members)); } @@ -444,7 +447,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty path to do FindMembersByPath")); } - value nc_block_member_descriptor; + auto nc_block_member_descriptors = value::array(); for (const auto& role : path) { @@ -459,14 +462,13 @@ namespace nmos if (members.end() != member_found) { - nc_block_member_descriptor = *member_found; + web::json::push_back(nc_block_member_descriptors, *member_found); - // use oid to look for next resource + // use oid to look for the next resource resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(*member_found)))); } else { - // should // no role utility::stringstream_t ss; ss << U("role: ") << role.as_string() << U(" not found to do FindMembersByPath"); @@ -475,13 +477,12 @@ namespace nmos } else { - // should // no members return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, U("no members to do FindMembersByPath")); } } - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, nc_block_member_descriptor); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, nc_block_member_descriptors); } // resource not found for the given oid From 5eeaa3b7e4734c83f4685cac8c50697b702547de Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 11 Aug 2023 14:34:33 +0100 Subject: [PATCH 018/250] Add FindMembersByRole and FindMembersByClassId --- Development/cmake/NmosCppLibraries.cmake | 2 + .../nmos/control_protocol_resource.cpp | 4 +- Development/nmos/control_protocol_resource.h | 2 +- Development/nmos/control_protocol_utils.cpp | 170 ++++++++++++++++++ Development/nmos/control_protocol_utils.h | 17 ++ Development/nmos/control_protocol_ws_api.cpp | 122 +++++++++---- 6 files changed, 283 insertions(+), 34 deletions(-) create mode 100644 Development/nmos/control_protocol_utils.cpp create mode 100644 Development/nmos/control_protocol_utils.h diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index cce8c420b..07a7b4df1 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -835,6 +835,7 @@ set(NMOS_CPP_NMOS_SOURCES nmos/control_protocol_resource.cpp nmos/control_protocol_resources.cpp nmos/control_protocol_state.cpp + nmos/control_protocol_utils.cpp nmos/control_protocol_ws_api.cpp nmos/did_sdid.cpp nmos/events_api.cpp @@ -913,6 +914,7 @@ set(NMOS_CPP_NMOS_HEADERS nmos/control_protocol_resource.h nmos/control_protocol_resources.h nmos/control_protocol_state.h + nmos/control_protocol_utils.h nmos/control_protocol_ws_api.h nmos/device_type.h nmos/did_sdid.h diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 974c51aba..be029d7f5 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -102,10 +102,10 @@ namespace nmos return nc_class_id; } - nc_class_id parse_nc_class_id(const web::json::value& class_id_) + nc_class_id parse_nc_class_id(const web::json::array& class_id_) { nc_class_id class_id; - for (auto& element : class_id_.as_array()) + for (auto& element : class_id_) { class_id.push_back(element.as_integer()); } diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index dca604afa..3aa7e7429 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -162,7 +162,7 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid web::json::value make_nc_class_id(const nc_class_id& class_id); - nc_class_id parse_nc_class_id(const web::json::value& class_id); + nc_class_id parse_nc_class_id(const web::json::array& class_id); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid web::json::value make_nc_element_id(uint16_t level, uint16_t index); diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp new file mode 100644 index 000000000..0b8326f0d --- /dev/null +++ b/Development/nmos/control_protocol_utils.cpp @@ -0,0 +1,170 @@ +#include "nmos/control_protocol_utils.h" + +#include +#include +#include +#include "cpprest/json_utils.h" +#include "nmos/json_fields.h" +#include "nmos/resources.h" + +#include "nmos/control_protocol_resource.h" // for nc_class_id + +namespace nmos +{ + namespace details + { + bool is_control_class(const nc_class_id& control_class_id, const nc_class_id& class_id_) + { + nc_class_id class_id{ class_id_ }; + if (control_class_id.size() < class_id.size()) + { + // truncate test class_id to relevant class_id + class_id.resize(control_class_id.size()); + } + return control_class_id == class_id; + } + + bool is_nc_block(const nc_class_id& class_id) + { + return is_control_class(nc_object_class_id, class_id); + } + + bool is_nc_manager(const nc_class_id& class_id) + { + return is_control_class(nc_manager_class_id, class_id); + } + + bool is_nc_device_manager(const nc_class_id& class_id) + { + return is_control_class(nc_device_manager_class_id, class_id); + } + + bool is_nc_class_manager(const nc_class_id& class_id) + { + return is_control_class(nc_class_manager_class_id, class_id); + } + } + + void get_member_descriptors(const resources& resources, resources::iterator resource, bool recurse, web::json::array& descriptors) + { + if (resource->data.has_field(nmos::fields::nc::members)) + { + const auto& members = nmos::fields::nc::members(resource->data); + + // hmm, maybe an easier way to apeend array to array + for (const auto& member : members) + { + web::json::push_back(descriptors, member); + } + + if (recurse) + { + // get members on all NcBlock(s) + for (const auto& member : members) + { + if (details::is_nc_block(details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + { + // get resource based on the oid + const auto& oid = nmos::fields::nc::oid(member); + + get_member_descriptors(resources, find_resource(resources, utility::s2us(std::to_string(oid))), recurse, descriptors); + } + } + } + } + } + + void find_members_by_role(const resources& resources, resources::iterator resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& descriptors) + { + auto find_members_by_matching_role = [&](const web::json::array& members) + { + using web::json::value; + + auto match = [&](const web::json::value& descriptor) + { + if (match_whole_string) + { + if (case_sensitive) { return role == nmos::fields::nc::role(descriptor); } + else { return boost::algorithm::to_upper_copy(role) == boost::algorithm::to_upper_copy(nmos::fields::nc::role(descriptor)); } + } + else + { + if (case_sensitive) { return !boost::find_first(nmos::fields::nc::role(descriptor), role).empty(); } + else { return !boost::ifind_first(nmos::fields::nc::role(descriptor), role).empty(); } + } + }; + + return boost::make_iterator_range(boost::make_filter_iterator(match, members.begin(), members.end()), boost::make_filter_iterator(match, members.end(), members.end())); + }; + + if (resource->data.has_field(nmos::fields::nc::members)) + { + const auto& members = nmos::fields::nc::members(resource->data); + + auto members_found = find_members_by_matching_role(members); + for (const auto& member : members_found) + { + web::json::push_back(descriptors, member); + } + + if (recurse) + { + // do role match on all NcBlock(s) + for (const auto& member : members) + { + if (details::is_nc_block(details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + { + // get resource based on the oid + const auto& oid = nmos::fields::nc::oid(member); + + find_members_by_role(resources, find_resource(resources, utility::s2us(std::to_string(oid))), role, match_whole_string, case_sensitive, recurse, descriptors); + } + } + } + } + } + + void find_members_by_class_id(const resources& resources, resources::iterator resource, const details::nc_class_id& class_id_, bool include_derived, bool recurse, web::json::array& descriptors) + { + auto find_members_by_matching_class_id = [&](const web::json::array& members) + { + using web::json::value; + + auto match = [&](const web::json::value& descriptor) + { + const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); + + if (include_derived) { return !boost::find_first(class_id, class_id_).empty(); } + else { return class_id == class_id_; } + }; + + return boost::make_iterator_range(boost::make_filter_iterator(match, members.begin(), members.end()), boost::make_filter_iterator(match, members.end(), members.end())); + }; + + if (resource->data.has_field(nmos::fields::nc::members)) + { + auto& members = nmos::fields::nc::members(resource->data); + + auto members_found = find_members_by_matching_class_id(members); + for (const auto& member : members_found) + { + web::json::push_back(descriptors, member); + } + + if (recurse) + { + // do class_id match on all NcBlock(s) + for (const auto& member : members) + { + if (details::is_nc_block(details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + { + // get resource based on the oid + const auto& oid = nmos::fields::nc::oid(member); + + find_members_by_class_id(resources, find_resource(resources, utility::s2us(std::to_string(oid))), class_id_, include_derived, recurse, descriptors); + } + } + } + } + } +} diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h new file mode 100644 index 000000000..fa9766aaf --- /dev/null +++ b/Development/nmos/control_protocol_utils.h @@ -0,0 +1,17 @@ +#ifndef NMOS_CONTROL_PROTOCOL_UTILS_H +#define NMOS_CONTROL_PROTOCOL_UTILS_H + +#include "cpprest/basic_utils.h" +#include "nmos/control_protocol_resource.h" // for nc_class_id definition +#include "nmos/resources.h" + +namespace nmos +{ + void get_member_descriptors(const resources& resources, resources::iterator resource, bool recurse, web::json::array& descriptors); + + void find_members_by_role(const resources& resources, resources::iterator resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& nc_block_member_descriptors); + + void find_members_by_class_id(const resources& resources, resources::iterator resource, const details::nc_class_id& class_id, bool include_derived, bool recurse, web::json::array& descriptors); +} + +#endif diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index a92e73fc9..091b7dce9 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -1,18 +1,16 @@ #include "nmos/control_protocol_ws_api.h" -#include #include -#include "bst/regex.h" #include "cpprest/json_validator.h" #include "cpprest/regex_utils.h" #include "nmos/api_utils.h" #include "nmos/control_protocol_resources.h" +#include "nmos/control_protocol_utils.h" #include "nmos/is12_versions.h" #include "nmos/json_schema.h" #include "nmos/model.h" #include "nmos/query_utils.h" #include "nmos/slog.h" -#include "nmos/resources.h" namespace nmos { @@ -81,13 +79,13 @@ namespace nmos // unknown property utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << " to do Get"; + ss << U("unknown property: ") << property_id.serialize() << U(" to do Get"); return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); } // resource not found for the given oid utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to do Get"; + ss << U("unknown oid: ") << oid << U(" to do Get"); return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Set property value @@ -164,33 +162,32 @@ namespace nmos { // property is not a sequence utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << " is not a sequence to do GetSequenceItem"; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceItem"); return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); } auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); - if (!data.is_null() && data.as_array().size() > index) + if (!data.is_null() && data.as_array().size() > (size_t)index) { return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, data.at(index)); } // out of bound utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << " is outside the available range to do GetSequenceItem"; + ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do GetSequenceItem"); return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); } // unknown property utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize( - ) << " to do GetSequenceItem"; + ss << U("unknown property: ") << property_id.serialize() << U(" to do GetSequenceItem"); return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); } // resource not found for the given oid utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to do GetSequenceItem"; + ss << U("unknown oid: ") << oid << U(" to do GetSequenceItem"); return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Set sequence item @@ -216,13 +213,13 @@ namespace nmos { // property is not a sequence utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << " is not a sequence to do SetSequenceItem"; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do SetSequenceItem"); return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); } auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); - if (!data.is_null() && data.as_array().size() > index) + if (!data.is_null() && data.as_array().size() > (size_t)index) { resources.modify(resource, [&](nmos::resource& resource) { @@ -235,13 +232,13 @@ namespace nmos // out of bound utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << " is outside the available range to do SetSequenceItem"; + ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do SetSequenceItem"); return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); } // resource not found for the given oid utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to do SetSequenceItem"; + ss << U("unknown oid: ") << oid << U(" to do SetSequenceItem"); return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Add item to sequence @@ -266,7 +263,7 @@ namespace nmos { // property is not a sequence utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << " is not a sequence to do AddSequenceItem"; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do AddSequenceItem"); return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); } @@ -285,7 +282,7 @@ namespace nmos // resource not found for the given oid utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to do AddSequenceItem"; + ss << U("unknown oid: ") << oid << U(" to do AddSequenceItem"); return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Delete sequence item @@ -310,13 +307,13 @@ namespace nmos { // property is not a sequence utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << " is not a sequence to do RemoveSequenceItem"; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do RemoveSequenceItem"); return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); } auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); - if (!data.is_null() && data.as_array().size() > index) + if (!data.is_null() && data.as_array().size() > (size_t)index) { resources.modify(resource, [&](nmos::resource& resource) { @@ -330,13 +327,13 @@ namespace nmos // out of bound utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << " is outside the available range to do RemoveSequenceItem"; + ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do RemoveSequenceItem"); return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); } // resource not found for the given oid utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to do RemoveSequenceItem"; + ss << U("unknown oid: ") << oid << U(" to do RemoveSequenceItem"); return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Get sequence length @@ -409,23 +406,23 @@ namespace nmos // Gets descriptors of members of the block const auto get_member_descriptors = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments) { + const auto& recurse = nmos::fields::nc::recurse(arguments); // If recurse is set to true, nested members is to be retrieved + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... auto& resources = model.control_protocol_resources; auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != resource) { - // where arguments is the boolean recurse value - // hmm, If recurse is set to true, nested members is to be retrieved - const auto& recurse = nmos::fields::nc::recurse(arguments); + auto descriptors = value::array(); + nmos::get_member_descriptors(resources, nmos::find_resource(resources, utility::s2us(std::to_string(oid))), recurse, descriptors.as_array()); - // return the descriptors of members of the block - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, resource->data.at(nmos::fields::nc::members)); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptors); } // resource not found for the given oid utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to do GetMemberDescriptors"; + ss << U("unknown oid: ") << oid << U(" to do GetMemberDescriptors"); return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Finds member(s) by path @@ -490,6 +487,69 @@ namespace nmos ss << U("unknown oid: ") << oid << " to do FindMembersByPath"; return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; + // Finds members with given role name or fragment + const auto find_members_by_role = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments) + { + const auto& role = nmos::fields::nc::role(arguments); // Role text to search for + const auto& case_sensitive = nmos::fields::nc::case_sensitive(arguments); // Signals if the comparison should be case sensitive + const auto& match_whole_string = nmos::fields::nc::match_whole_string(arguments); // TRUE to only return exact matches + const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks + + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto& resources = model.control_protocol_resources; + + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + if (role.empty()) + { + // empty role + return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty role to do FindMembersByRole")); + } + + auto descriptors = value::array(); + nmos::find_members_by_role(resources, nmos::find_resource(resources, utility::s2us(std::to_string(oid))), role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); + + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptors); + } + + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid << U(" to do FindMembersByRole"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + }; + // Finds members with given class id + const auto find_members_by_class_id = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments) + { + const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for + const auto& include_derived = nmos::fields::nc::include_derived(arguments); // If TRUE it will also include derived class descriptors + const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks + + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto& resources = model.control_protocol_resources; + + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + if (class_id.empty()) + { + // empty class_id + return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty classId to do FindMembersByClassId")); + } + + auto descriptors = value::array(); + nmos::find_members_by_class_id(resources, nmos::find_resource(resources, utility::s2us(std::to_string(oid))), class_id, include_derived, recurse, descriptors.as_array()); + + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptors); + } + + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid << U(" to do FindMembersByClassId"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + }; // NcClassManager methods implementation // Get a single class descriptor @@ -499,7 +559,7 @@ namespace nmos // resource not found for the given oid utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to get control class"; + ss << U("unknown oid: ") << oid << U(" to get control class"); return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; @@ -525,8 +585,8 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 1 } })] = get_member_descriptors; nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 2 } })] = find_members_by_path; - //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 3 } })] = find_members_by_role; - //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 4 } })] = find_members_by_class_id; + nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 3 } })] = find_members_by_role; + nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 4 } })] = find_members_by_class_id; // NcWorker has no extended method // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker @@ -801,7 +861,7 @@ namespace nmos if (resources.end() != resource) { // create the combined properties and method handlers based on class_id - auto class_id = details::parse_nc_class_id(resource->data.at(nmos::fields::nc::class_id)); + auto class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); auto properties_methods = details::create_properties_methods(model, class_id, get_control_protocol_classes()); auto& properties = properties_methods.first; auto& methods = properties_methods.second; From 5d5e2b8d0b91a106f43e5c070dd362499c6173bb Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 15 Aug 2023 18:22:11 +0100 Subject: [PATCH 019/250] Add GetControlClass, and GetDataType --- Development/cmake/NmosCppLibraries.cmake | 2 + Development/nmos-cpp-node/main.cpp | 1 + .../nmos/control_protocol_class_id.cpp | 27 ++ Development/nmos/control_protocol_class_id.h | 19 + .../nmos/control_protocol_handlers.cpp | 48 ++- Development/nmos/control_protocol_handlers.h | 15 +- .../nmos/control_protocol_resource.cpp | 29 +- Development/nmos/control_protocol_resource.h | 60 ++- .../nmos/control_protocol_resources.cpp | 6 +- Development/nmos/control_protocol_state.cpp | 62 ++- Development/nmos/control_protocol_state.h | 38 +- Development/nmos/control_protocol_utils.cpp | 3 +- Development/nmos/control_protocol_utils.h | 13 +- Development/nmos/control_protocol_ws_api.cpp | 372 ++++++++++++------ Development/nmos/control_protocol_ws_api.h | 6 +- Development/nmos/node_server.cpp | 2 +- Development/nmos/node_server.h | 5 +- 17 files changed, 509 insertions(+), 199 deletions(-) create mode 100644 Development/nmos/control_protocol_class_id.cpp create mode 100644 Development/nmos/control_protocol_class_id.h diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index 07a7b4df1..a135f74d5 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -831,6 +831,7 @@ set(NMOS_CPP_NMOS_SOURCES nmos/connection_api.cpp nmos/connection_events_activation.cpp nmos/connection_resources.cpp + nmos/control_protocol_class_id.cpp nmos/control_protocol_handlers.cpp nmos/control_protocol_resource.cpp nmos/control_protocol_resources.cpp @@ -910,6 +911,7 @@ set(NMOS_CPP_NMOS_HEADERS nmos/connection_api.h nmos/connection_events_activation.h nmos/connection_resources.h + nmos/control_protocol_class_id.h nmos/control_protocol_handlers.h nmos/control_protocol_resource.h nmos/control_protocol_resources.h diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index 60b0cc56b..159ef35da 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -111,6 +111,7 @@ int main(int argc, char* argv[]) nmos::experimental::control_protocol_state control_protocol_state; node_implementation.on_get_control_classes(nmos::make_get_control_protocol_classes_handler(control_protocol_state, gate)); + node_implementation.on_get_control_datatypes(nmos::make_get_control_protocol_datatypes_handler(control_protocol_state, gate)); // Set up the node server diff --git a/Development/nmos/control_protocol_class_id.cpp b/Development/nmos/control_protocol_class_id.cpp new file mode 100644 index 000000000..8ca0ec90c --- /dev/null +++ b/Development/nmos/control_protocol_class_id.cpp @@ -0,0 +1,27 @@ +#include "nmos/control_protocol_class_id.h" + +namespace nmos +{ + namespace details + { + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid + web::json::value make_nc_class_id(const nc_class_id& class_id) + { + using web::json::value; + + auto nc_class_id = value::array(); + for (const auto class_id_item : class_id) { web::json::push_back(nc_class_id, class_id_item); } + return nc_class_id; + } + + nc_class_id parse_nc_class_id(const web::json::array& class_id_) + { + nc_class_id class_id; + for (auto& element : class_id_) + { + class_id.push_back(element.as_integer()); + } + return class_id; + } + } +} diff --git a/Development/nmos/control_protocol_class_id.h b/Development/nmos/control_protocol_class_id.h new file mode 100644 index 000000000..f48bbe731 --- /dev/null +++ b/Development/nmos/control_protocol_class_id.h @@ -0,0 +1,19 @@ +#ifndef NMOS_CONTROL_PROTOCOL_CLASS_ID_H +#define NMOS_CONTROL_PROTOCOL_CLASS_ID_H + +#include "cpprest/json_utils.h" + +namespace nmos +{ + namespace details + { + // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid + typedef std::vector nc_class_id; + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid + web::json::value make_nc_class_id(const nc_class_id& class_id); + nc_class_id parse_nc_class_id(const web::json::array& class_id); + } +} + +#endif diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index 3f1738c1e..609053ea4 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -1,7 +1,5 @@ #include "nmos/control_protocol_handlers.h" -#include "cpprest/basic_utils.h" -#include "nmos/control_protocol_state.h" #include "nmos/slog.h" namespace nmos @@ -18,28 +16,28 @@ namespace nmos }; } - get_control_protocol_class_handler make_get_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) - { - return [&](const details::nc_class_id& class_id) - { - using web::json::value; + //get_control_protocol_class_handler make_get_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) + //{ + // return [&](const details::nc_class_id& class_id) + // { + // using web::json::value; - slog::log(gate, SLOG_FLF) << "Retrieve control class from cache"; + // slog::log(gate, SLOG_FLF) << "Retrieve control class from cache"; - auto lock = control_protocol_state.read_lock(); + // auto lock = control_protocol_state.read_lock(); - auto class_id_data = details::make_nc_class_id(class_id); + // auto class_id_data = details::make_nc_class_id(class_id); - auto& control_classes = control_protocol_state.control_classes; - auto found = control_classes.find(class_id_data); - if (control_classes.end() != found) - { - return found->second; - } + // auto& control_classes = control_protocol_state.control_classes; + // auto found = control_classes.find(class_id_data); + // if (control_classes.end() != found) + // { + // return found->second; + // } - return experimental::control_class{ value::array(), value::array(), value::array() }; - }; - } + // return experimental::control_class{ value::array(), value::array(), value::array() }; + // }; + //} add_control_protocol_class_handler make_add_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) { @@ -61,4 +59,16 @@ namespace nmos return true; }; } + + get_control_protocol_datatypes_handler make_get_control_protocol_datatypes_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) + { + return [&]() + { + slog::log(gate, SLOG_FLF) << "Retrieve all datatypes from cache"; + + auto lock = control_protocol_state.read_lock(); + + return control_protocol_state.datatypes; + }; + } } diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index 1478235b4..d0af0c30e 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -1,9 +1,7 @@ #ifndef NMOS_CONTROL_PROTOCOL_HANDLERS_H #define NMOS_CONTROL_PROTOCOL_HANDLERS_H -#include #include -#include "nmos/control_protocol_resource.h" #include "nmos/control_protocol_state.h" namespace slog @@ -15,8 +13,8 @@ namespace nmos { namespace experimental { - struct control_class; struct control_protocol_state; + struct control_class; } // callback to retrieve all control protocol classes @@ -25,20 +23,27 @@ namespace nmos // callback to retrieve a specific control protocol class // this callback should not throw exceptions - typedef std::function get_control_protocol_class_handler; +// typedef std::function get_control_protocol_class_handler; // callback to add user control protocol class // this callback should not throw exceptions typedef std::function add_control_protocol_class_handler; + // callback to retrieve all control protocol datatypes + // this callback should not throw exceptions + typedef std::function get_control_protocol_datatypes_handler; + // construct callback to retrieve all control protocol classes get_control_protocol_classes_handler make_get_control_protocol_classes_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); // construct callback to retrieve control protocol class - get_control_protocol_class_handler make_get_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); +// get_control_protocol_class_handler make_get_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); // construct callback to add control protocol class add_control_protocol_class_handler make_add_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); + + // construct callback to retrieve all datatypes + get_control_protocol_datatypes_handler make_get_control_protocol_datatypes_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); } #endif diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index be029d7f5..9a98eb4cf 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -1,6 +1,7 @@ #include "nmos/control_protocol_resource.h" //#include "nmos/resource.h" +#include "nmos/control_protocol_state.h" // for nmos::experimental::control_classes definitions #include "nmos/json_fields.h" namespace nmos @@ -92,26 +93,6 @@ namespace nmos }); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid - web::json::value make_nc_class_id(const nc_class_id& class_id) - { - using web::json::value; - - auto nc_class_id = value::array(); - for (const auto class_id_item : class_id) { web::json::push_back(nc_class_id, class_id_item); } - return nc_class_id; - } - - nc_class_id parse_nc_class_id(const web::json::array& class_id_) - { - nc_class_id class_id; - for (auto& element : class_id_) - { - class_id.push_back(element.as_integer()); - } - return class_id; - } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid web::json::value make_nc_element_id(uint16_t level, uint16_t index) { @@ -795,7 +776,7 @@ namespace nmos return make_nc_datatype_descriptor_struct(value::string(U("Id method result")), U("NcMethodResultId"), value::null(), fields, value::string(U("NcMethodResult"))); } - web::json::value make_method_result_length_datatype() + web::json::value make_nc_method_result_length_datatype() { using web::json::value; @@ -1152,11 +1133,11 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label) + web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { using web::json::value; - auto data = make_nc_manager(nc_class_manager_class_id, oid, true, owner, U("ClassManager"), user_label); + auto data = make_nc_manager(nc_class_manager_class_id, oid, true, owner, U("ClassManager"), user_label, touchpoints, runtime_property_constraints); // minimal control classes data[nmos::fields::nc::control_classes] = value::array(); @@ -1203,7 +1184,7 @@ namespace nmos // NcMethodResultId web::json::push_back(datatypes, make_nc_method_result_id_datatype()); // NcMethodResultLength - web::json::push_back(datatypes, make_method_result_length_datatype()); + web::json::push_back(datatypes, make_nc_method_result_length_datatype()); // NcPropertyChangeType web::json::push_back(datatypes, make_nc_property_change_type_datatype()); // NcPropertyChangedEventData diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 3aa7e7429..999e37aff 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -3,6 +3,8 @@ #include #include "cpprest/json_utils.h" +#include "nmos/control_protocol_class_id.h" +#include "nmos/control_protocol_state.h" // for nmos::experimental::control_classes definitions namespace web { @@ -126,7 +128,6 @@ namespace nmos typedef utility::string_t nc_uuid; // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid - typedef std::vector nc_class_id; const nc_class_id nc_object_class_id({ 1 }); const nc_class_id nc_block_class_id({ 1, 1 }); const nc_class_id nc_worker_class_id({ 1, 2 }); @@ -137,9 +138,7 @@ namespace nmos // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nctouchpoint typedef utility::string_t nc_touch_point; - typedef std::map properties; - - typedef std::function method; + typedef std::function method; typedef std::map methods; // method_id vs method handler web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message); @@ -160,10 +159,6 @@ namespace nmos // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#error-messages web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid - web::json::value make_nc_class_id(const nc_class_id& class_id); - nc_class_id parse_nc_class_id(const web::json::array& class_id); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid web::json::value make_nc_element_id(uint16_t level, uint16_t index); @@ -202,7 +197,7 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassdescriptor // description can be null // fixedRole can be null - web::json::value make_nc_class_descriptor(const web::json::value& description, const web::json::value& class_id, const utility::string_t& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); + web::json::value make_nc_class_descriptor(const web::json::value& description, const nc_class_id& class_id, const utility::string_t& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncenumitemdescriptor // description can be null @@ -266,7 +261,7 @@ namespace nmos // constraints can be null web::json::value make_nc_datatype_typedef(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const utility::string_t& parent_type, bool is_sequence); - // make the core classes proprties/methods/events + // make the core control classes proprties/methods/events web::json::value make_nc_object_properties(); web::json::value make_nc_object_methods(); web::json::value make_nc_object_events(); @@ -286,6 +281,47 @@ namespace nmos web::json::value make_nc_class_manager_methods(); web::json::value make_nc_class_manager_events(); + // make the core datatypes + web::json::value make_nc_class_id_datatype(); + web::json::value make_nc_oid_datatype(); + web::json::value make_nc_touchpoint_datatype(); + web::json::value make_nc_element_id_datatype(); + web::json::value make_nc_property_id_datatype(); + web::json::value make_nc_property_contraints_datatype(); + web::json::value make_nc_method_result_property_value_datatype(); + web::json::value make_nc_method_status_datatype(); + web::json::value make_nc_method_result_datatype(); + web::json::value make_nc_id_datatype(); + web::json::value make_nc_method_result_id_datatype(); + web::json::value make_nc_method_result_length_datatype(); + web::json::value make_nc_property_change_type_datatype(); + web::json::value make_nc_property_changed_event_data_datatype(); + web::json::value make_nc_descriptor_datatype(); + web::json::value make_nc_block_member_descriptor_datatype(); + web::json::value make_nc_method_result_block_member_descriptors_datatype(); + web::json::value make_nc_version_code_datatype(); + web::json::value make_nc_organization_id_datatype(); + web::json::value make_nc_uri_datatype(); + web::json::value make_nc_manufacturer_datatype(); + web::json::value make_nc_uuid_datatype(); + web::json::value make_nc_product_datatype(); + web::json::value make_nc_device_generic_state_datatype(); + web::json::value make_nc_device_operational_state_datatype(); + web::json::value make_nc_reset_cause_datatype(); + web::json::value make_nc_name_datatype(); + web::json::value make_nc_property_descriptor_datatype(); + web::json::value make_nc_parameter_descriptor_datatype(); + web::json::value make_nc_method_id_datatype(); + web::json::value make_nc_method_descriptor_datatype(); + web::json::value make_nc_event_id_datatype(); + web::json::value make_nc_event_descriptor_datatype(); + web::json::value make_nc_class_descriptor_datatype(); + web::json::value make_nc_parameter_constraints_datatype(); + web::json::value make_nc_datatype_type_datatype(); + web::json::value make_nc_datatype_descriptor_datatype(); + web::json::value make_nc_method_result_class_descriptor_datatype(); + web::json::value make_nc_method_result_datatype_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); @@ -293,7 +329,7 @@ namespace nmos web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager - web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null()); + web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, @@ -301,7 +337,7 @@ namespace nmos const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager(details::nc_oid oid, nc_oid owner, const web::json::value& user_label); + web::json::value make_nc_class_manager(details::nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); } } diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index f5e2754b4..6c89b6f5a 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -26,8 +26,8 @@ namespace nmos manufacturer, product, serial_number, value::null(), device_name, device_role, operational_state, details::nc_reset_cause::Unknown); // add NcDeviceManager block_member_descriptor to root block members - web::json::push_back(root_block_data[nmos::fields::nc::members], details::make_nc_block_member_descriptor( - description, nmos::fields::nc::role(data), oid, nmos::fields::nc::constant_oid(data), data.at(nmos::fields::nc::class_id), user_label, owner)); + web::json::push_back(root_block_data[nmos::fields::nc::members], + details::make_nc_block_member_descriptor(description, nmos::fields::nc::role(data), oid, nmos::fields::nc::constant_oid(data), data.at(nmos::fields::nc::class_id), user_label, owner)); return{ is12_versions::v1_0, types::nc_device_manager, std::move(data), true }; } @@ -42,7 +42,7 @@ namespace nmos const auto user_label = value::string(U("Class manager")); const auto description = value::string(U("The class manager offers access to control class and data type descriptors")); - auto data = details::make_nc_class_manager(oid, owner, user_label); + auto data = details::make_nc_class_manager(oid, owner, user_label, value::null(), value::null()); // add NcClassManager block_member_descriptor to root block members web::json::push_back(root_block_data[nmos::fields::nc::members], diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index c420072be..7270591a3 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -1,6 +1,6 @@ #include "nmos/control_protocol_state.h" -#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_resource.h" // for nc_object_class_id, nc_block_class_id, nc_worker_class_id, nc_manager_class_id, nc_device_manager_class_id, nc_class_manager_class_id definitions namespace nmos { @@ -8,15 +8,61 @@ namespace nmos { control_protocol_state::control_protocol_state() { - // setup the core control classes (properties/methods/events) + using web::json::value; + + // setup the core control classes control_classes = { - { details::make_nc_class_id(details::nc_object_class_id), { details::make_nc_object_properties(), details::make_nc_object_methods(), details::make_nc_object_events() } }, - { details::make_nc_class_id(details::nc_block_class_id), { details::make_nc_block_properties(), details::make_nc_block_methods(), details::make_nc_block_events() } }, - { details::make_nc_class_id(details::nc_worker_class_id), { details::make_nc_worker_properties(), details::make_nc_worker_methods(), details::make_nc_worker_events() } }, - { details::make_nc_class_id(details::nc_manager_class_id), { details::make_nc_manager_properties(), details::make_nc_manager_methods(), details::make_nc_manager_events() } }, - { details::make_nc_class_id(details::nc_device_manager_class_id), { details::make_nc_device_manager_properties(), details::make_nc_device_manager_methods(), details::make_nc_device_manager_events() } }, - { details::make_nc_class_id(details::nc_class_manager_class_id), { details::make_nc_class_manager_properties(), details::make_nc_class_manager_methods(), details::make_nc_class_manager_events() } } + { details::make_nc_class_id(details::nc_object_class_id), { value::string(U("NcObject class descriptor")), details::nc_object_class_id, U("NcObject"), value::null(), details::make_nc_object_properties(), details::make_nc_object_methods(), details::make_nc_object_events() } }, + { details::make_nc_class_id(details::nc_block_class_id), { value::string(U("NcBlock class descriptor")), details::nc_block_class_id, U("NcBlock"), value::null(), details::make_nc_block_properties(), details::make_nc_block_methods(), details::make_nc_block_events() } }, + { details::make_nc_class_id(details::nc_worker_class_id), { value::string(U("NcWorker class descriptor")), details::nc_worker_class_id, U("NcWorker"), value::null(), details::make_nc_worker_properties(), details::make_nc_worker_methods(), details::make_nc_worker_events() } }, + { details::make_nc_class_id(details::nc_manager_class_id), { value::string(U("NcManager class descriptor")), details::nc_manager_class_id, U("NcManager"), value::null(), details::make_nc_manager_properties(), details::make_nc_manager_methods(), details::make_nc_manager_events() } }, + { details::make_nc_class_id(details::nc_device_manager_class_id), { value::string(U("NcDeviceManager class descriptor")), details::nc_device_manager_class_id, U("NcDeviceManager"), value::string(U("DeviceManager")), details::make_nc_device_manager_properties(), details::make_nc_device_manager_methods(), details::make_nc_device_manager_events() } }, + { details::make_nc_class_id(details::nc_class_manager_class_id), { value::string(U("NcClassManager class descriptor")), details::nc_class_manager_class_id, U("NcClassManager"), value::string(U("ClassManager")), details::make_nc_class_manager_properties(), details::make_nc_class_manager_methods(), details::make_nc_class_manager_events() } } + }; + + // setup the core datatypes + datatypes = + { + { U("NcClassId"), {details::make_nc_class_id_datatype()} }, + { U("NcOid"), {details::make_nc_oid_datatype()} }, + { U("NcTouchpoint"), {details::make_nc_touchpoint_datatype()} }, + { U("NcElementId"), {details::make_nc_element_id_datatype()} }, + { U("NcPropertyId"), {details::make_nc_property_id_datatype()} }, + { U("NcPropertyConstraints"), {details::make_nc_property_contraints_datatype()} }, + { U("NcMethodResultPropertyValue"), {details::make_nc_method_result_property_value_datatype()} }, + { U("NcMethodStatus"), {details::make_nc_method_status_datatype()} }, + { U("NcMethodResult"), {details::make_nc_method_result_datatype()} }, + { U("NcId"), {details::make_nc_id_datatype()} }, + { U("NcMethodResultId"), {details::make_nc_method_result_id_datatype()} }, + { U("NcMethodResultLength"), {details::make_nc_method_result_length_datatype()} }, + { U("NcPropertyChangeType"), {details::make_nc_property_change_type_datatype()} }, + { U("NcPropertyChangedEventData"), {details::make_nc_property_changed_event_data_datatype()} }, + { U("NcDescriptor"), {details::make_nc_descriptor_datatype()} }, + { U("NcBlockMemberDescriptor"), {details::make_nc_block_member_descriptor_datatype()} }, + { U("NcMethodResultBlockMemberDescriptors"), {details::make_nc_method_result_block_member_descriptors_datatype()} }, + { U("NcVersionCode"), {details::make_nc_version_code_datatype()} }, + { U("NcOrganizationId"), {details::make_nc_organization_id_datatype()} }, + { U("NcUri"), {details::make_nc_uri_datatype()} }, + { U("NcManufacturer"), {details::make_nc_manufacturer_datatype()} }, + { U("NcUuid"), {details::make_nc_uuid_datatype()} }, + { U("NcProduct"), {details::make_nc_product_datatype()} }, + { U("NcDeviceGenericState"), {details::make_nc_device_generic_state_datatype()} }, + { U("NcDeviceOperationalState"), {details::make_nc_device_operational_state_datatype()} }, + { U("NcResetCause"), {details::make_nc_reset_cause_datatype()} }, + { U("NcName"), {details::make_nc_name_datatype()} }, + { U("NcPropertyDescriptor"), {details::make_nc_property_descriptor_datatype()} }, + { U("NcParameterDescriptor"), {details::make_nc_parameter_descriptor_datatype()} }, + { U("NcMethodId"), {details::make_nc_method_id_datatype()} }, + { U("NcMethodDescriptor"), {details::make_nc_method_descriptor_datatype()} }, + { U("NcEventId"), {details::make_nc_event_id_datatype()} }, + { U("NcEventDescriptor"), {details::make_nc_event_descriptor_datatype()} }, + { U("NcClassDescriptor"), {details::make_nc_class_descriptor_datatype()} }, + { U("NcParameterConstraints"), {details::make_nc_parameter_constraints_datatype()} }, + { U("NcDatatypeType"), {details::make_nc_datatype_type_datatype()} }, + { U("NcDatatypeDescriptor"), {details::make_nc_datatype_descriptor_datatype()} }, + { U("NcMethodResultClassDescriptor"), {details::make_nc_method_result_class_descriptor_datatype()} }, + { U("NcMethodResultDatatypeDescriptor"), {details::make_nc_method_result_datatype_descriptor_datatype()} } }; } } diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index ad884733c..0dfde5990 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -3,20 +3,55 @@ #include #include "cpprest/json_utils.h" +#include "nmos/control_protocol_class_id.h" // for nmos::details::nc_class_id definitions #include "nmos/mutex.h" namespace nmos { namespace experimental { - struct control_class + struct control_class // NcClassDescriptor { + web::json::value description; + nmos::details::nc_class_id class_id; + utility::string_t name; + web::json::value fixed_role; + web::json::value properties; // array of nc_property_descriptor web::json::value methods; // array of nc_method_descriptor web::json::value events; // array of nc_event_descriptor + + //control_class(details::nc_class_id class_id, utility::string_t name, web::json::value fixed_role, web::json::value properties, web::json::value methods, web::json::value events) + // : description(web::json::value::null()) + // , class_id(std::move(class_id)) + // , name(std::move(name)) + // , fixed_role(std::move(fixed_role)) + // , properties(std::move(properties)) + // , methods(std::move(methods)) + // , events(std::move(events)) + //{} + + //control_class(const utility::string_t& description, details::nc_class_id class_id, utility::string_t name, web::json::value fixed_role, web::json::value properties, web::json::value methods, web::json::value events) + // : description(web::json::value::string(description)) + // , class_id(std::move(class_id)) + // , name(std::move(name)) + // , fixed_role(std::move(fixed_role)) + // , properties(std::move(properties)) + // , methods(std::move(methods)) + // , events(std::move(events)) + //{} + + }; + + struct datatype // NcDatatypeDescriptorEnum/NcDatatypeDescriptorPrimitive/NcDatatypeDescriptorStruct/NcDatatypeDescriptorTypeDef + { + web::json::value descriptor; }; + // nc_class_id vs control_class typedef std::map control_classes; + // nc_name vs datatype + typedef std::map datatypes; struct control_protocol_state { @@ -24,6 +59,7 @@ namespace nmos mutable nmos::mutex mutex; experimental::control_classes control_classes; + experimental::datatypes datatypes; nmos::read_lock read_lock() const { return nmos::read_lock{ mutex }; } nmos::write_lock write_lock() const { return nmos::write_lock{ mutex }; } diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 0b8326f0d..106a55a7d 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -4,11 +4,10 @@ #include #include #include "cpprest/json_utils.h" +#include "nmos/control_protocol_resource.h" // for nc_object_class_id, nc_manager_class_id, nc_device_manager_class_id, nc_class_manager_class_id #include "nmos/json_fields.h" #include "nmos/resources.h" -#include "nmos/control_protocol_resource.h" // for nc_class_id - namespace nmos { namespace details diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index fa9766aaf..b90f63574 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -2,11 +2,22 @@ #define NMOS_CONTROL_PROTOCOL_UTILS_H #include "cpprest/basic_utils.h" -#include "nmos/control_protocol_resource.h" // for nc_class_id definition +#include "nmos/control_protocol_class_id.h" // for nc_class_id definition #include "nmos/resources.h" namespace nmos { + namespace details + { + bool is_nc_block(const nc_class_id& class_id); + + bool is_nc_manager(const nc_class_id& class_id); + + bool is_nc_device_manager(const nc_class_id& class_id); + + bool is_nc_class_manager(const nc_class_id& class_id); + } + void get_member_descriptors(const resources& resources, resources::iterator resource, bool recurse, web::json::array& descriptors); void find_members_by_role(const resources& resources, resources::iterator resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& nc_block_member_descriptors); diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 091b7dce9..c51b90655 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -55,7 +55,7 @@ namespace nmos // NcObject methods implementation // Get property value - const auto get = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + const auto get = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -71,8 +71,7 @@ namespace nmos { return property_id == nmos::fields::nc::id(property); }); - - if (property_found != properties.end()) + if (properties.end() != property_found) { return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(*property_found))); } @@ -89,7 +88,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Set property value - const auto set = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + const auto set = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -105,7 +104,7 @@ namespace nmos { return property_id == nmos::fields::nc::id(property); }); - if (property_found != properties.end()) + if (properties.end() != property_found) { if (nmos::fields::nc::is_read_only(*property_found)) { @@ -139,7 +138,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Get sequence item - const auto get_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + const auto get_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -155,8 +154,7 @@ namespace nmos { return property_id == nmos::fields::nc::id(property); }); - - if (property_found != properties.end()) + if (properties.end() != property_found) { if (!nmos::fields::nc::is_sequence(*property_found)) { @@ -191,7 +189,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Set sequence item - const auto set_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + const auto set_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -208,32 +206,39 @@ namespace nmos { return property_id == nmos::fields::nc::id(property); }); - - if (!nmos::fields::nc::is_sequence(*property_found)) + if (properties.end() != property_found) { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do SetSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); - } + if (!nmos::fields::nc::is_sequence(*property_found)) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do SetSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } - auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); + auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); - if (!data.is_null() && data.as_array().size() > (size_t)index) - { - resources.modify(resource, [&](nmos::resource& resource) + if (!data.is_null() && data.as_array().size() > (size_t)index) { - resource.data[nmos::fields::nc::name(*property_found)][index] = val; + resources.modify(resource, [&](nmos::resource& resource) + { + resource.data[nmos::fields::nc::name(*property_found)][index] = val; - resource.updated = strictly_increasing_update(resources); - }); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); + resource.updated = strictly_increasing_update(resources); + }); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); + } + + // out of bound + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do SetSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); } - // out of bound + // unknown property utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do SetSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); + ss << U("unknown property: ") << property_id.serialize() << U(" to do SetSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); } // resource not found for the given oid @@ -242,7 +247,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Add item to sequence - const auto add_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + const auto add_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -258,26 +263,33 @@ namespace nmos { return property_id == nmos::fields::nc::id(property); }); - - if (!nmos::fields::nc::is_sequence(*property_found)) + if (properties.end() != property_found) { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do AddSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); - } + if (!nmos::fields::nc::is_sequence(*property_found)) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do AddSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } - auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); + auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); - resources.modify(resource, [&](nmos::resource& resource) - { - auto& sequence = resource.data[nmos::fields::nc::name(*property_found)]; - if (data.is_null()) { sequence = value::array(); } - web::json::push_back(sequence, val); + resources.modify(resource, [&](nmos::resource& resource) + { + auto& sequence = resource.data[nmos::fields::nc::name(*property_found)]; + if (data.is_null()) { sequence = value::array(); } + web::json::push_back(sequence, val); - resource.updated = strictly_increasing_update(resources); - }); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, uint32_t(data.as_array().size() - 1)); + resource.updated = strictly_increasing_update(resources); + }); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, uint32_t(data.as_array().size() - 1)); + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do AddSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); } // resource not found for the given oid @@ -286,7 +298,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Delete sequence item - const auto remove_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + const auto remove_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -302,33 +314,40 @@ namespace nmos { return property_id == nmos::fields::nc::id(property); }); - - if (!nmos::fields::nc::is_sequence(*property_found)) + if (properties.end() != property_found) { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do RemoveSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); - } + if (!nmos::fields::nc::is_sequence(*property_found)) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do RemoveSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } - auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); + auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); - if (!data.is_null() && data.as_array().size() > (size_t)index) - { - resources.modify(resource, [&](nmos::resource& resource) + if (!data.is_null() && data.as_array().size() > (size_t)index) { - auto& sequence = resource.data[nmos::fields::nc::name(*property_found)].as_array(); - sequence.erase(index); + resources.modify(resource, [&](nmos::resource& resource) + { + auto& sequence = resource.data[nmos::fields::nc::name(*property_found)].as_array(); + sequence.erase(index); - resource.updated = strictly_increasing_update(resources); - }); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); + resource.updated = strictly_increasing_update(resources); + }); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); + } + + // out of bound + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do RemoveSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); } - // out of bound + // unknown property utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do RemoveSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); + ss << U("unknown property: ") << property_id.serialize() << U(" to do RemoveSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); } // resource not found for the given oid @@ -337,7 +356,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Get sequence length - const auto get_sequence_length = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + const auto get_sequence_length = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -355,39 +374,37 @@ namespace nmos if (property_found != properties.end()) { - if (nmos::fields::nc::is_sequence(*property_found)) + if (!nmos::fields::nc::is_sequence(*property_found)) { - auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceLength"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } - if (nmos::fields::nc::is_nullable(*property_found)) - { - // can be null - if (data.is_null()) - { - // null - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, value::null()); - } - } - else + auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); + + if (nmos::fields::nc::is_nullable(*property_found)) + { + // can be null + if (data.is_null()) { - // cannot be null - if (data.is_null()) - { - // null - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << " is a null sequence to do GetSequenceLength"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); - } + // null + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, value::null()); } - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, uint32_t(data.as_array().size())); } else { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << " is not a sequence to do GetSequenceLength"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + // cannot be null + if (data.is_null()) + { + // null + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << " is a null sequence to do GetSequenceLength"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } } + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, uint32_t(data.as_array().size())); } // unknown property @@ -404,16 +421,16 @@ namespace nmos // NcBlock methods implementation // Gets descriptors of members of the block - const auto get_member_descriptors = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments) + const auto get_member_descriptors = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { - const auto& recurse = nmos::fields::nc::recurse(arguments); // If recurse is set to true, nested members is to be retrieved - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... auto& resources = model.control_protocol_resources; auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != resource) { + const auto& recurse = nmos::fields::nc::recurse(arguments); // If recurse is set to true, nested members is to be retrieved + auto descriptors = value::array(); nmos::get_member_descriptors(resources, nmos::find_resource(resources, utility::s2us(std::to_string(oid))), recurse, descriptors.as_array()); @@ -426,12 +443,11 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Finds member(s) by path - const auto find_members_by_path = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments) + const auto find_members_by_path = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... auto& resources = model.control_protocol_resources; - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != resource) { @@ -488,7 +504,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Finds members with given role name or fragment - const auto find_members_by_role = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments) + const auto find_members_by_role = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { const auto& role = nmos::fields::nc::role(arguments); // Role text to search for const auto& case_sensitive = nmos::fields::nc::case_sensitive(arguments); // Signals if the comparison should be case sensitive @@ -520,12 +536,18 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Finds members with given class id - const auto find_members_by_class_id = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments) + const auto find_members_by_class_id = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for const auto& include_derived = nmos::fields::nc::include_derived(arguments); // If TRUE it will also include derived class descriptors const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks + if (class_id.empty()) + { + // empty class_id + return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty classId to do FindMembersByClassId")); + } + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... auto& resources = model.control_protocol_resources; @@ -533,12 +555,6 @@ namespace nmos auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != resource) { - if (class_id.empty()) - { - // empty class_id - return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty classId to do FindMembersByClassId")); - } - auto descriptors = value::array(); nmos::find_members_by_class_id(resources, nmos::find_resource(resources, utility::s2us(std::to_string(oid))), class_id, include_derived, recurse, descriptors.as_array()); @@ -553,13 +569,131 @@ namespace nmos // NcClassManager methods implementation // Get a single class descriptor - const auto get_control_class = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments) + const auto get_control_class = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + { + const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for + const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements + + if (class_id.empty()) + { + // empty class_id + return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty classId to do GetControlClass")); + } + + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto& resources = model.control_protocol_resources; + + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + auto class_found = control_classes.find(make_nc_class_id(class_id)); + + if (control_classes.end() != class_found) + { + auto id = class_id; + + auto description = class_found->second.description; + auto name = class_found->second.name; + auto fixed_role = class_found->second.fixed_role; + auto properties = class_found->second.properties; + auto methods = class_found->second.methods; + auto events = class_found->second.events; + + id.pop_back(); + + if (include_inherited) + { + while (!id.empty()) + { + auto found = control_classes.find(make_nc_class_id(id)); + if (control_classes.end() != found) + { + for (const auto& property : found->second.properties.as_array()) { web::json::push_back(properties, property); } + for (const auto& method : found->second.methods.as_array()) { web::json::push_back(methods, method); } + for (const auto& event : found->second.events.as_array()) { web::json::push_back(events, event); } + } + id.pop_back(); + } + } + auto descriptor = details::make_nc_class_descriptor(description, class_id, name, fixed_role, properties, methods, events); + + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptor); + } + + return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("classId not found")); + } + + // resource not found for the given oid + utility::stringstream_t ss; + ss << U("unknown oid: ") << oid << U(" to do GetControlClass"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + }; + // Get a single datatype descriptor + const auto get_datatype = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { - // hmm, todo + const auto& name = nmos::fields::nc::name(arguments); // name of datatype + const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements + + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto& resources = model.control_protocol_resources; + + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + if (name.empty()) + { + // empty name + return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty name to do GetDatatype")); + } + + auto datatype_found = datatypes.find(name); + + if (datatypes.end() != datatype_found) + { + auto descriptor = datatype_found->second.descriptor; + + if (include_inherited) + { + const auto& type = nmos::fields::nc::type(descriptor); + if(details::nc_datatype_type::Struct == type) + { + auto descriptor_ = descriptor; + + for (;;) + { + const auto& parent_type = descriptor_.at(nmos::fields::nc::parent_type); + if (!parent_type.is_null()) + { + auto datatype_found_ = datatypes.find(parent_type.as_string()); + if (datatypes.end() != datatype_found_) + { + descriptor_ = datatype_found_->second.descriptor; + const auto& fields = nmos::fields::nc::fields(descriptor_); + for (const auto& field : fields) + { + web::json::push_back(descriptor.at(nmos::fields::nc::fields), field); + } + } + } + else + { + break; + } + } + } + } + + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptor); + } + + return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("name not found")); + } // resource not found for the given oid utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << U(" to get control class"); + ss << U("unknown oid: ") << oid << U(" to do GetDatatype"); return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; @@ -599,23 +733,23 @@ namespace nmos // NcClassManager methods // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 1 } })] = get_control_class; - //nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 2 } })] = get_datatype; + nc_class_manager_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 1 } })] = get_control_class; + nc_class_manager_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 2 } })] = get_datatype; value properties = value::array(); // combined base classes nc_property_descriptor(s) for the required class_id details::methods methods; // list of combined base classes method handlers - auto found_class = control_classes.find(make_nc_class_id(class_id_)); - if (control_classes.end() != found_class) + auto class_found = control_classes.find(make_nc_class_id(class_id_)); + if (control_classes.end() != class_found) { // hmm, update the array of properties, will be updated the list of method handlers auto insert_properties = [&properties, &control_classes](const nc_class_id& class_id_) { auto class_id = make_nc_class_id(class_id_); - auto found = control_classes.find(class_id); - if (control_classes.end() != found) + auto class_id_found = control_classes.find(class_id); + if (control_classes.end() != class_id_found) { - auto& nc_class_properties = found->second.properties.as_array(); + auto& nc_class_properties = class_id_found->second.properties.as_array(); for (auto& nc_class_property : nc_class_properties) { web::json::push_back(properties, nc_class_property); @@ -637,7 +771,7 @@ namespace nmos { methods.insert(nc_block_method_handlers.begin(), nc_block_method_handlers.end()); } - else if (details::nc_device_manager_class_id == class_id) + else if (details::nc_manager_class_id == class_id) { methods.insert(nc_manager_method_handlers.begin(), nc_manager_method_handlers.end()); } @@ -797,11 +931,11 @@ namespace nmos }; } - web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_classes_handler get_control_protocol_classes, slog::base_gate& gate_) + web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_classes_handler get_control_protocol_classes, nmos::get_control_protocol_datatypes_handler get_control_protocol_datatypes, slog::base_gate& gate_) { using web::json::value; - return [&model, &websockets, get_control_protocol_classes, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id, const web::websockets::websocket_incoming_message& msg_) + return [&model, &websockets, get_control_protocol_classes, get_control_protocol_datatypes, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id, const web::websockets::websocket_incoming_message& msg_) { nmos::ws_api_gate gate(gate_, connection_uri); @@ -867,11 +1001,11 @@ namespace nmos auto& methods = properties_methods.second; // find the relevent method handler to execute - auto method = methods.find(method_id); - if (method != methods.end()) + auto method_found = methods.find(method_id); + if (method_found != methods.end()) { // execute the relevant method handler, then accumulating up their response to reponses - web::json::push_back(responses, method->second(properties.as_array(), handle, oid, arguments)); + web::json::push_back(responses, method_found->second(properties.as_array(), handle, oid, arguments, get_control_protocol_classes(), get_control_protocol_datatypes())); } else { diff --git a/Development/nmos/control_protocol_ws_api.h b/Development/nmos/control_protocol_ws_api.h index 61c434f38..6f0494ae1 100644 --- a/Development/nmos/control_protocol_ws_api.h +++ b/Development/nmos/control_protocol_ws_api.h @@ -16,15 +16,15 @@ namespace nmos web::websockets::experimental::listener::validate_handler make_control_protocol_ws_validate_handler(nmos::node_model& model, slog::base_gate& gate); web::websockets::experimental::listener::open_handler make_control_protocol_ws_open_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate); web::websockets::experimental::listener::close_handler make_control_protocol_ws_close_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate); - web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_classes_handler get_control_protocol_classes, slog::base_gate& gate); + web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_classes_handler get_control_protocol_classes, nmos::get_control_protocol_datatypes_handler get_control_protocol_datatypes, slog::base_gate& gate); - inline web::websockets::experimental::listener::websocket_listener_handlers make_control_protocol_ws_api(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_classes_handler get_control_protocol_classes, slog::base_gate& gate) + inline web::websockets::experimental::listener::websocket_listener_handlers make_control_protocol_ws_api(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_classes_handler get_control_protocol_classes, nmos::get_control_protocol_datatypes_handler get_control_protocol_datatypes, slog::base_gate& gate) { return{ nmos::make_control_protocol_ws_validate_handler(model, gate), nmos::make_control_protocol_ws_open_handler(model, websockets, gate), nmos::make_control_protocol_ws_close_handler(model, websockets, gate), - nmos::make_control_protocol_ws_message_handler(model, websockets, get_control_protocol_classes, gate) + nmos::make_control_protocol_ws_message_handler(model, websockets, get_control_protocol_classes, get_control_protocol_datatypes, gate) }; } diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index 0ac70ee42..d163fdea8 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -72,7 +72,7 @@ namespace nmos const auto& control_protocol_ws_port = nmos::fields::control_protocol_ws_port(node_model.settings); if (control_protocol_ws_port == events_ws_port) throw std::runtime_error("Same port used for events and control protocol websockets are not supported"); auto& control_protocol_ws_api = node_server.ws_handlers[{ {}, control_protocol_ws_port }]; - control_protocol_ws_api.first = nmos::make_control_protocol_ws_api(node_model, control_protocol_ws_api.second, node_implementation.get_control_protocol_classes, gate); + control_protocol_ws_api.first = nmos::make_control_protocol_ws_api(node_model, control_protocol_ws_api.second, node_implementation.get_control_protocol_classes, node_implementation.get_control_protocol_datatypes, gate); // Set up the listeners for each HTTP API port diff --git a/Development/nmos/node_server.h b/Development/nmos/node_server.h index eb1b95bc7..4f897a8e0 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -25,7 +25,7 @@ namespace nmos // underlying implementation into the server instance for the NMOS Node struct node_implementation { - node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, nmos::get_control_protocol_classes_handler get_control_protocol_classes) + node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, nmos::get_control_protocol_classes_handler get_control_protocol_classes, nmos::get_control_protocol_datatypes_handler get_control_protocol_datatypes) : load_server_certificates(std::move(load_server_certificates)) , load_dh_param(std::move(load_dh_param)) , load_ca_certificates(std::move(load_ca_certificates)) @@ -38,6 +38,7 @@ namespace nmos , connection_activated(std::move(connection_activated)) , get_ocsp_response(std::move(get_ocsp_response)) , get_control_protocol_classes(std::move(get_control_protocol_classes)) + , get_control_protocol_datatypes(std::move(get_control_protocol_datatypes)) {} // use the default constructor and chaining member functions for fluent initialization @@ -60,6 +61,7 @@ namespace nmos node_implementation& on_channelmapping_activated(nmos::channelmapping_activation_handler channelmapping_activated) { this->channelmapping_activated = std::move(channelmapping_activated); return *this; } node_implementation& on_get_ocsp_response(nmos::ocsp_response_handler get_ocsp_response) { this->get_ocsp_response = std::move(get_ocsp_response); return *this; } node_implementation& on_get_control_classes(nmos::get_control_protocol_classes_handler get_control_protocol_classes) { this->get_control_protocol_classes = std::move(get_control_protocol_classes); return* this; } + node_implementation& on_get_control_datatypes(nmos::get_control_protocol_datatypes_handler get_control_protocol_datatypes) { this->get_control_protocol_datatypes = std::move(get_control_protocol_datatypes); return*this; } // deprecated, use on_validate_connection_resource_patch node_implementation& on_validate_merged(nmos::details::connection_resource_patch_validator validate_merged) { return on_validate_connection_resource_patch(std::move(validate_merged)); } @@ -91,6 +93,7 @@ namespace nmos nmos::ocsp_response_handler get_ocsp_response; nmos::get_control_protocol_classes_handler get_control_protocol_classes; + nmos::get_control_protocol_datatypes_handler get_control_protocol_datatypes; }; // Construct a server instance for an NMOS Node, implementing the IS-04 Node API, IS-05 Connection API, IS-07 Events API From 6eddf4ed68b7c22a802a8fa4c49e8d00da50267c Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 16 Aug 2023 11:09:45 +0100 Subject: [PATCH 020/250] Bump up ubuntu 14.04 to use python 3.7 to overcome ERROR: module 'asyncio' has no attribute 'get_running_loop' and CryptographyDeprecationWarning: Python 3.6 is no longer supported by the Python core team --- .github/workflows/build-test.yml | 10 +++++----- .github/workflows/src/build-test.yml | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index ed8737899..c5e35acd4 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -579,13 +579,13 @@ jobs: apt-get --allow-unauthenticated install -y curl g++ git make patch zlib1g-dev libssl-dev bsdmainutils dnsutils unzip # ubuntu-14.04 ca-certificates are out of date git config --global http.sslVerify false - curl -sS https://www.python.org/ftp/python/3.6.9/Python-3.6.9.tar.xz | tar -xJ - cd Python-3.6.9 + curl -sS https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tar.xz | tar -xJ + cd Python-3.7.0 ./configure make -j8 make install - update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.6 3 - ln -s /usr/local/bin/python3.6 /usr/bin/python + update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.7 3 + ln -s /usr/local/bin/python3.7 /usr/bin/python curl -sS https://bootstrap.pypa.io/pip/3.6/get-pip.py | python curl -sS https://nodejs.org/dist/v12.16.2/node-v12.16.2-linux-x64.tar.xz | tar -xJ echo "`pwd`/node-v12.16.2-linux-x64/bin" >> $GITHUB_PATH @@ -1075,4 +1075,4 @@ jobs: git config --global user.name 'test-results-uploader' git config --global user.email 'test-results-uploader@nmos-cpp.iam.gserviceaccount.com' git commit -qm "Badges for README at ${{ env.GITHUB_COMMIT }}" - git push -f `git remote` badges-${{ env.GITHUB_COMMIT }}:badges + git push -f `git remote` badges-${{ env.GITHUB_COMMIT }}:badges \ No newline at end of file diff --git a/.github/workflows/src/build-test.yml b/.github/workflows/src/build-test.yml index 0b5663ff3..9e3c26b5c 100644 --- a/.github/workflows/src/build-test.yml +++ b/.github/workflows/src/build-test.yml @@ -129,13 +129,13 @@ jobs: apt-get --allow-unauthenticated install -y curl g++ git make patch zlib1g-dev libssl-dev bsdmainutils dnsutils unzip # ubuntu-14.04 ca-certificates are out of date git config --global http.sslVerify false - curl -sS https://www.python.org/ftp/python/3.6.9/Python-3.6.9.tar.xz | tar -xJ - cd Python-3.6.9 + curl -sS https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tar.xz | tar -xJ + cd Python-3.7.0 ./configure make -j8 make install - update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.6 3 - ln -s /usr/local/bin/python3.6 /usr/bin/python + update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.7 3 + ln -s /usr/local/bin/python3.7 /usr/bin/python curl -sS https://bootstrap.pypa.io/pip/3.6/get-pip.py | python curl -sS https://nodejs.org/dist/v12.16.2/node-v12.16.2-linux-x64.tar.xz | tar -xJ echo "`pwd`/node-v12.16.2-linux-x64/bin" >> $GITHUB_PATH From fa17cd110ab69d986b3bb4f8c58d0a5ea424e482 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 16 Aug 2023 11:26:58 +0100 Subject: [PATCH 021/250] Fix ModuleNotFoundError: No module named '_ctypes' --- .github/workflows/build-test.yml | 2 +- .github/workflows/src/build-test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index c5e35acd4..27490c47e 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -576,7 +576,7 @@ jobs: apt-get update -q apt-get install -y software-properties-common apt-get --allow-unauthenticated update -q - apt-get --allow-unauthenticated install -y curl g++ git make patch zlib1g-dev libssl-dev bsdmainutils dnsutils unzip + apt-get --allow-unauthenticated install -y curl g++ git make patch zlib1g-dev libssl-dev bsdmainutils dnsutils unzip libffi-dev # ubuntu-14.04 ca-certificates are out of date git config --global http.sslVerify false curl -sS https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tar.xz | tar -xJ diff --git a/.github/workflows/src/build-test.yml b/.github/workflows/src/build-test.yml index 9e3c26b5c..2cd99001d 100644 --- a/.github/workflows/src/build-test.yml +++ b/.github/workflows/src/build-test.yml @@ -126,7 +126,7 @@ jobs: apt-get update -q apt-get install -y software-properties-common apt-get --allow-unauthenticated update -q - apt-get --allow-unauthenticated install -y curl g++ git make patch zlib1g-dev libssl-dev bsdmainutils dnsutils unzip + apt-get --allow-unauthenticated install -y curl g++ git make patch zlib1g-dev libssl-dev bsdmainutils dnsutils unzip libffi-dev # ubuntu-14.04 ca-certificates are out of date git config --global http.sslVerify false curl -sS https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tar.xz | tar -xJ From a356f3efa15bd0adc6690ce8fadc705dffa451bb Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 16 Aug 2023 11:42:54 +0100 Subject: [PATCH 022/250] Fixing python3.7 install --- .github/workflows/build-test.yml | 2 +- .github/workflows/src/build-test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 27490c47e..117ab340d 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -576,7 +576,7 @@ jobs: apt-get update -q apt-get install -y software-properties-common apt-get --allow-unauthenticated update -q - apt-get --allow-unauthenticated install -y curl g++ git make patch zlib1g-dev libssl-dev bsdmainutils dnsutils unzip libffi-dev + apt-get --allow-unauthenticated install -y curl g++ git make patch zlib1g-dev libssl-dev bsdmainutils dnsutils unzip libffi-dev libreadline-gplv2-dev libncursesw5-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev # ubuntu-14.04 ca-certificates are out of date git config --global http.sslVerify false curl -sS https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tar.xz | tar -xJ diff --git a/.github/workflows/src/build-test.yml b/.github/workflows/src/build-test.yml index 2cd99001d..e4f254f23 100644 --- a/.github/workflows/src/build-test.yml +++ b/.github/workflows/src/build-test.yml @@ -126,7 +126,7 @@ jobs: apt-get update -q apt-get install -y software-properties-common apt-get --allow-unauthenticated update -q - apt-get --allow-unauthenticated install -y curl g++ git make patch zlib1g-dev libssl-dev bsdmainutils dnsutils unzip libffi-dev + apt-get --allow-unauthenticated install -y curl g++ git make patch zlib1g-dev libssl-dev bsdmainutils dnsutils unzip libffi-dev libreadline-gplv2-dev libncursesw5-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev # ubuntu-14.04 ca-certificates are out of date git config --global http.sslVerify false curl -sS https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tar.xz | tar -xJ From 15a4e19dbdf518f5d2a222fc2776e1fb69a8dbe0 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 16 Aug 2023 11:56:49 +0100 Subject: [PATCH 023/250] Bump up to python3.8 --- .github/workflows/build-test.yml | 10 +++++----- .github/workflows/src/build-test.yml | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 117ab340d..4df6c575d 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -576,16 +576,16 @@ jobs: apt-get update -q apt-get install -y software-properties-common apt-get --allow-unauthenticated update -q - apt-get --allow-unauthenticated install -y curl g++ git make patch zlib1g-dev libssl-dev bsdmainutils dnsutils unzip libffi-dev libreadline-gplv2-dev libncursesw5-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev + apt-get --allow-unauthenticated install -y curl g++ git make patch zlib1g-dev libssl-dev bsdmainutils dnsutils unzip libffi-dev # ubuntu-14.04 ca-certificates are out of date git config --global http.sslVerify false - curl -sS https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tar.xz | tar -xJ - cd Python-3.7.0 + curl -sS https://www.python.org/ftp/python/3.8.0/Python-3.8.0.tar.xz | tar -xJ + cd Python-3.8.0 ./configure make -j8 make install - update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.7 3 - ln -s /usr/local/bin/python3.7 /usr/bin/python + update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.8 3 + ln -s /usr/local/bin/python3.8 /usr/bin/python curl -sS https://bootstrap.pypa.io/pip/3.6/get-pip.py | python curl -sS https://nodejs.org/dist/v12.16.2/node-v12.16.2-linux-x64.tar.xz | tar -xJ echo "`pwd`/node-v12.16.2-linux-x64/bin" >> $GITHUB_PATH diff --git a/.github/workflows/src/build-test.yml b/.github/workflows/src/build-test.yml index e4f254f23..be9e9d513 100644 --- a/.github/workflows/src/build-test.yml +++ b/.github/workflows/src/build-test.yml @@ -126,16 +126,16 @@ jobs: apt-get update -q apt-get install -y software-properties-common apt-get --allow-unauthenticated update -q - apt-get --allow-unauthenticated install -y curl g++ git make patch zlib1g-dev libssl-dev bsdmainutils dnsutils unzip libffi-dev libreadline-gplv2-dev libncursesw5-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev + apt-get --allow-unauthenticated install -y curl g++ git make patch zlib1g-dev libssl-dev bsdmainutils dnsutils unzip libffi-dev # ubuntu-14.04 ca-certificates are out of date git config --global http.sslVerify false - curl -sS https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tar.xz | tar -xJ - cd Python-3.7.0 + curl -sS https://www.python.org/ftp/python/3.8.0/Python-3.8.0.tar.xz | tar -xJ + cd Python-3.8.0 ./configure make -j8 make install - update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.7 3 - ln -s /usr/local/bin/python3.7 /usr/bin/python + update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.8 3 + ln -s /usr/local/bin/python3.8 /usr/bin/python curl -sS https://bootstrap.pypa.io/pip/3.6/get-pip.py | python curl -sS https://nodejs.org/dist/v12.16.2/node-v12.16.2-linux-x64.tar.xz | tar -xJ echo "`pwd`/node-v12.16.2-linux-x64/bin" >> $GITHUB_PATH From 70155a41a6e77b20bd651091581caa36c8507b8c Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 18 Aug 2023 10:43:03 +0100 Subject: [PATCH 024/250] Add NcIdentBeacon, NcReceiverMonitor and NcReceiverMonitorProtected --- Development/nmos-cpp-node/main.cpp | 2 +- .../nmos-cpp-node/node_implementation.cpp | 11 +- .../nmos-cpp-node/node_implementation.h | 3 +- .../nmos/control_protocol_resource.cpp | 921 +++++++++++------- Development/nmos/control_protocol_resource.h | 270 +++-- .../nmos/control_protocol_resources.cpp | 4 +- Development/nmos/control_protocol_resources.h | 7 +- Development/nmos/control_protocol_state.cpp | 38 +- Development/nmos/control_protocol_state.h | 26 +- Development/nmos/control_protocol_ws_api.cpp | 717 ++++++++++++-- Development/nmos/json_fields.h | 14 + 11 files changed, 1504 insertions(+), 509 deletions(-) diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index 159ef35da..5b17eebb5 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -129,7 +129,7 @@ int main(int argc, char* argv[]) // Add the underlying implementation, which will set up the node resources, etc. - node_server.thread_functions.push_back([&] { node_implementation_thread(node_model, gate); }); + node_server.thread_functions.push_back([&] { node_implementation_thread(node_model, control_protocol_state, gate); }); // only implement communication with OCSP server if http_listener supports OCSP stapling // cf. preprocessor conditions in nmos::make_http_listener_config diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index e72dcc24b..d232d3bf3 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -22,6 +22,7 @@ #include "nmos/connection_resources.h" #include "nmos/connection_events_activation.h" #include "nmos/control_protocol_resources.h" +#include "nmos/control_protocol_state.h" #include "nmos/events_resources.h" #include "nmos/format.h" #include "nmos/group_hint.h" @@ -187,7 +188,7 @@ namespace impl } // forward declarations for node_implementation_thread -void node_implementation_init(nmos::node_model& model, slog::base_gate& gate); +void node_implementation_init(nmos::node_model& model, const nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); void node_implementation_run(nmos::node_model& model, slog::base_gate& gate); nmos::connection_resource_auto_resolver make_node_implementation_auto_resolver(const nmos::settings& settings); nmos::connection_sender_transportfile_setter make_node_implementation_transportfile_setter(const nmos::resources& node_resources, const nmos::settings& settings); @@ -197,13 +198,13 @@ struct node_implementation_init_exception {}; // This is an example of how to integrate the nmos-cpp library with a device-specific underlying implementation. // It constructs and inserts a node resource and some sub-resources into the model, based on the model settings, // starts background tasks to emit regular events from the temperature event source, and then waits for shutdown. -void node_implementation_thread(nmos::node_model& model, slog::base_gate& gate_) +void node_implementation_thread(nmos::node_model& model, const nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate_) { nmos::details::omanip_gate gate{ gate_, nmos::stash_category(impl::categories::node_implementation) }; try { - node_implementation_init(model, gate); + node_implementation_init(model, control_protocol_state, gate); node_implementation_run(model, gate); } catch (const node_implementation_init_exception&) @@ -233,7 +234,7 @@ void node_implementation_thread(nmos::node_model& model, slog::base_gate& gate_) } } -void node_implementation_init(nmos::node_model& model, slog::base_gate& gate) +void node_implementation_init(nmos::node_model& model, const nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) { using web::json::value; using web::json::value_from_elements; @@ -902,7 +903,7 @@ void node_implementation_init(nmos::node_model& model, slog::base_gate& gate) auto device_manager = nmos::make_device_manager(2, root_block, model.settings); if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(device_manager), gate)) throw node_implementation_init_exception(); // example class manager - auto class_manager = nmos::make_class_manager(3, root_block); + auto class_manager = nmos::make_class_manager(3, root_block, control_protocol_state); if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(class_manager), gate)) throw node_implementation_init_exception(); // insert root block to model if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(root_block), gate)) throw node_implementation_init_exception(); diff --git a/Development/nmos-cpp-node/node_implementation.h b/Development/nmos-cpp-node/node_implementation.h index 421769f38..c5d6504da 100644 --- a/Development/nmos-cpp-node/node_implementation.h +++ b/Development/nmos-cpp-node/node_implementation.h @@ -13,13 +13,14 @@ namespace nmos namespace experimental { struct node_implementation; + struct control_protocol_state; } } // This is an example of how to integrate the nmos-cpp library with a device-specific underlying implementation. // It constructs and inserts a node resource and some sub-resources into the model, based on the model settings, // starts background tasks to emit regular events from the temperature event source, and then waits for shutdown. -void node_implementation_thread(nmos::node_model& model, slog::base_gate& gate); +void node_implementation_thread(nmos::node_model& model, const nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); // This constructs all the callbacks used to integrate the example device-specific underlying implementation // into the server instance for the NMOS Node. diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 9a98eb4cf..b456443f7 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -334,7 +334,7 @@ namespace nmos // description can be null // constraints can be null // items: sequence - web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const web::json::value& items) + web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const utility::string_t& name, const web::json::value& items, const web::json::value& constraints) { auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Enum, constraints); data[nmos::fields::nc::items] = items; @@ -355,7 +355,7 @@ namespace nmos // constraints can be null // fields: sequence // parent_type can be null - web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const web::json::value& fields, const web::json::value& parent_type) + web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const utility::string_t& name, const web::json::value& fields, const web::json::value& parent_type, const web::json::value& constraints) { auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Struct, constraints); data[nmos::fields::nc::fields] = fields; @@ -367,7 +367,7 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptortypedef // description can be null // constraints can be null - web::json::value make_nc_datatype_typedef(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const utility::string_t& parent_type, bool is_sequence) + web::json::value make_nc_datatype_typedef(const web::json::value& description, const utility::string_t& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints) { using web::json::value; @@ -378,23 +378,23 @@ namespace nmos return data; } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject web::json::value make_nc_object_properties() { using web::json::value; auto properties = value::array(); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Static value. All instances of the same class will have the same identity value")), make_nc_property_id(1, 1), nmos::fields::nc::class_id, value::string(U("NcClassId")), true, false, false, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Object identifier")), make_nc_property_id(1, 2), nmos::fields::nc::oid, value::string(U("NcOid")), true, false, false, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("TRUE iff OID is hardwired into device")), make_nc_property_id(1, 3), nmos::fields::nc::constant_oid, value::string(U("NcBoolean")), true, false, false, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("OID of containing block. Can only ever be null for the root block")), make_nc_property_id(1, 4), nmos::fields::nc::owner, value::string(U("NcOid")), true, true, false, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Role of object in the containing block")), make_nc_property_id(1, 5), nmos::fields::nc::role, value::string(U("NcString")), true, false, false, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Scribble strip")), make_nc_property_id(1, 6), nmos::fields::nc::user_label, value::string(U("NcString")), false, true, false, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Touchpoints to other contexts")), make_nc_property_id(1, 7), nmos::fields::nc::touchpoints, value::string(U("NcTouchpoint")), true, true, true, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Runtime property constraints")), make_nc_property_id(1, 8), nmos::fields::nc::runtime_property_constraints, value::string(U("NcPropertyConstraints")), true, true, true, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Static value. All instances of the same class will have the same identity value")), make_nc_property_id(1, 1), nmos::fields::nc::class_id, value::string(U("NcClassId")), true, false, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Object identifier")), make_nc_property_id(1, 2), nmos::fields::nc::oid, value::string(U("NcOid")), true, false, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("TRUE iff OID is hardwired into device")), make_nc_property_id(1, 3), nmos::fields::nc::constant_oid, value::string(U("NcBoolean")), true, false, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("OID of containing block. Can only ever be null for the root block")), make_nc_property_id(1, 4), nmos::fields::nc::owner, value::string(U("NcOid")), true, true, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Role of object in the containing block")), make_nc_property_id(1, 5), nmos::fields::nc::role, value::string(U("NcString")), true, false, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Scribble strip")), make_nc_property_id(1, 6), nmos::fields::nc::user_label, value::string(U("NcString")), false, true, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Touchpoints to other contexts")), make_nc_property_id(1, 7), nmos::fields::nc::touchpoints, value::string(U("NcTouchpoint")), true, true, true, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Runtime property constraints")), make_nc_property_id(1, 8), nmos::fields::nc::runtime_property_constraints, value::string(U("NcPropertyConstraints")), true, true, true, false)); return properties; } - web::json::value make_nc_object_methods() { using web::json::value; @@ -402,49 +402,48 @@ namespace nmos auto methods = value::array(); { auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Get property value")), make_nc_method_id(1, 1), U("Get"), U("NcMethodResultPropertyValue"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property value")), nmos::fields::nc::value, value::null(), true, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property value")), nmos::fields::nc::value, value::null(), true, false)); web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Set property value")), make_nc_method_id(1, 2), U("Set"), U("NcMethodResult"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false)); web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Get sequence item")), make_nc_method_id(1, 3), U("GetSequenceItem"), U("NcMethodResultPropertyValue"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false, value::null())); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Value")), nmos::fields::nc::value, value::null(), true, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false)); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Value")), nmos::fields::nc::value, value::null(), true, false)); web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Set sequence item value")), make_nc_method_id(1, 4), U("SetSequenceItem"), U("NcMethodResult"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Value")), nmos::fields::nc::value, value::null(), true, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Value")), nmos::fields::nc::value, value::null(), true, false)); web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Add item to sequence")), make_nc_method_id(1, 5), U("AddSequenceItem"), U("NcMethodResultId"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false)); web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Delete sequence item")), make_nc_method_id(1, 6), U("RemoveSequenceItem"), U("NcMethodResult"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Get sequence length")), make_nc_method_id(1, 7), U("GetSequenceLength"), U("NcMethodResultLength"), parameters, false)); } return methods; } - web::json::value make_nc_object_events() { using web::json::value; @@ -455,17 +454,17 @@ namespace nmos return events; } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock web::json::value make_nc_block_properties() { using web::json::value; auto properties = value::array(); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("TRUE if block is functional")), make_nc_property_id(2, 1), nmos::fields::nc::enabled, value::string(U("NcBoolean")), true, false, false, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Descriptors of this block's members")), make_nc_property_id(2, 2), nmos::fields::nc::members, value::string(U("NcBlockMemberDescriptor")), true, false, true, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("TRUE if block is functional")), make_nc_property_id(2, 1), nmos::fields::nc::enabled, value::string(U("NcBoolean")), true, false, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Descriptors of this block's members")), make_nc_property_id(2, 2), nmos::fields::nc::members, value::string(U("NcBlockMemberDescriptor")), true, false, true, false)); return properties; } - web::json::value make_nc_block_methods() { using web::json::value; @@ -473,33 +472,32 @@ namespace nmos auto methods = value::array(); { auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("If recurse is set to true, nested members can be retrieved")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("If recurse is set to true, nested members can be retrieved")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false)); web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Gets descriptors of members of the block")), make_nc_method_id(2, 1), U("GetMemberDescriptors"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Relative path to search for (MUST not include the role of the block targeted by oid)")), nmos::fields::nc::path, value::string(U("NcRolePath")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Relative path to search for (MUST not include the role of the block targeted by oid)")), nmos::fields::nc::path, value::string(U("NcRolePath")), false, false)); web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Finds member(s) by path")), make_nc_method_id(2, 2), U("FindMembersByPath"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Role text to search for")), nmos::fields::nc::role, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Signals if the comparison should be case sensitive")), nmos::fields::nc::case_sensitive, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("TRUE to only return exact matches")), nmos::fields::nc::match_whole_string, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("TRUE to search nested blocks")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Role text to search for")), nmos::fields::nc::role, value::string(U("NcString")), false, false)); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Signals if the comparison should be case sensitive")), nmos::fields::nc::case_sensitive, value::string(U("NcBoolean")), false, false)); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("TRUE to only return exact matches")), nmos::fields::nc::match_whole_string, value::string(U("NcBoolean")), false, false)); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("TRUE to search nested blocks")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false)); web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Finds members with given role name or fragment")), make_nc_method_id(2, 3), U("FindMembersByRole"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Class id to search for")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("If TRUE it will also include derived class descriptors")), nmos::fields::nc::include_derived, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("TRUE to search nested blocks")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Class id to search for")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false)); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("If TRUE it will also include derived class descriptors")), nmos::fields::nc::include_derived, value::string(U("NcBoolean")), false, false)); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("TRUE to search nested blocks")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false)); web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Finds members with given class id")), details::make_nc_method_id(2, 4), U("FindMembersByClassId"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); } return methods; } - web::json::value make_nc_block_events() { using web::json::value; @@ -507,23 +505,22 @@ namespace nmos return value::array(); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker web::json::value make_nc_worker_properties() { using web::json::value; auto properties = value::array(); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("TRUE iff worker is enabled")), make_nc_property_id(2, 1), nmos::fields::nc::enabled, value::string(U("NcBoolean")), false, false, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("TRUE iff worker is enabled")), make_nc_property_id(2, 1), nmos::fields::nc::enabled, value::string(U("NcBoolean")), false, false, false, false)); return properties; } - web::json::value make_nc_worker_methods() { using web::json::value; return value::array(); } - web::json::value make_nc_worker_events() { using web::json::value; @@ -531,20 +528,19 @@ namespace nmos return value::array(); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager web::json::value make_nc_manager_properties() { using web::json::value; return value::array(); } - web::json::value make_nc_manager_methods() { using web::json::value; return value::array(); } - web::json::value make_nc_manager_events() { using web::json::value; @@ -552,32 +548,31 @@ namespace nmos return value::array(); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager web::json::value make_nc_device_manager_properties() { using web::json::value; auto properties = value::array(); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Version of MS-05-02 that this device uses")), make_nc_property_id(3, 1), nmos::fields::nc::nc_version, value::string(U("NcVersionCode")), true, false, false, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Manufacturer descriptor")), make_nc_property_id(3, 2), nmos::fields::nc::manufacturer, value::string(U("NcManufacturer")), true, false, false, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Product descriptor")), make_nc_property_id(3, 3), nmos::fields::nc::product, value::string(U("NcProduct")), true, false, false, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Serial number")), make_nc_property_id(3, 4), nmos::fields::nc::serial_number, value::string(U("NcString")), true, false, false, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Asset tracking identifier (user specified)")), make_nc_property_id(3, 5), nmos::fields::nc::user_inventory_code, value::string(U("NcString")), false, true, false, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Name of this device in the application. Instance name, not product name")), make_nc_property_id(3, 6), nmos::fields::nc::device_name, value::string(U("NcString")), false, true, false, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Role of this device in the application")), make_nc_property_id(3, 7), nmos::fields::nc::device_role, value::string(U("NcString")), false, true, false, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Device operational state")), make_nc_property_id(3, 8), nmos::fields::nc::operational_state, value::string(U("NcDeviceOperationalState")), true, false, false, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Reason for most recent reset")), make_nc_property_id(3, 9), nmos::fields::nc::reset_cause, value::string(U("NcResetCause")), true, false, false, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Arbitrary message from dev to controller")), make_nc_property_id(3, 10), nmos::fields::nc::message, value::string(U("NcString")), true, true, false, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Version of MS-05-02 that this device uses")), make_nc_property_id(3, 1), nmos::fields::nc::nc_version, value::string(U("NcVersionCode")), true, false, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Manufacturer descriptor")), make_nc_property_id(3, 2), nmos::fields::nc::manufacturer, value::string(U("NcManufacturer")), true, false, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Product descriptor")), make_nc_property_id(3, 3), nmos::fields::nc::product, value::string(U("NcProduct")), true, false, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Serial number")), make_nc_property_id(3, 4), nmos::fields::nc::serial_number, value::string(U("NcString")), true, false, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Asset tracking identifier (user specified)")), make_nc_property_id(3, 5), nmos::fields::nc::user_inventory_code, value::string(U("NcString")), false, true, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Name of this device in the application. Instance name, not product name")), make_nc_property_id(3, 6), nmos::fields::nc::device_name, value::string(U("NcString")), false, true, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Role of this device in the application")), make_nc_property_id(3, 7), nmos::fields::nc::device_role, value::string(U("NcString")), false, true, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Device operational state")), make_nc_property_id(3, 8), nmos::fields::nc::operational_state, value::string(U("NcDeviceOperationalState")), true, false, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Reason for most recent reset")), make_nc_property_id(3, 9), nmos::fields::nc::reset_cause, value::string(U("NcResetCause")), true, false, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Arbitrary message from dev to controller")), make_nc_property_id(3, 10), nmos::fields::nc::message, value::string(U("NcString")), true, true, false, false)); return properties; } - web::json::value make_nc_device_manager_methods() { using web::json::value; return value::array(); } - web::json::value make_nc_device_manager_events() { using web::json::value; @@ -585,17 +580,17 @@ namespace nmos return value::array(); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager web::json::value make_nc_class_manager_properties() { using web::json::value; auto properties = value::array(); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Descriptions of all control classes in the device (descriptors do not contain inherited elements)")), make_nc_property_id(3, 1), nmos::fields::nc::control_classes, value::string(U("NcClassDescriptor")), true, false, true, false, value::null())); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Descriptions of all data types in the device (descriptors do not contain inherited elements)")), make_nc_property_id(3, 2), nmos::fields::nc::datatypes, value::string(U("NcDatatypeDescriptor")), true, false, true, false, value::null())); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Descriptions of all control classes in the device (descriptors do not contain inherited elements)")), make_nc_property_id(3, 1), nmos::fields::nc::control_classes, value::string(U("NcClassDescriptor")), true, false, true, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Descriptions of all data types in the device (descriptors do not contain inherited elements)")), make_nc_property_id(3, 2), nmos::fields::nc::datatypes, value::string(U("NcDatatypeDescriptor")), true, false, true, false)); return properties; } - web::json::value make_nc_class_manager_methods() { using web::json::value; @@ -603,20 +598,19 @@ namespace nmos auto methods = value::array(); { auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("class ID")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("If set the descriptor would contain all inherited elements")), nmos::fields::nc::include_inherited, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("class ID")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false)); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("If set the descriptor would contain all inherited elements")), nmos::fields::nc::include_inherited, value::string(U("NcBoolean")), false, false)); web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Get a single class descriptor")), make_nc_method_id(3, 1), U("GetControlClass"), U("NcMethodResultClassDescriptor"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("name of datatype")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("If set the descriptor would contain all inherited elements")), nmos::fields::nc::include_inherited, value::string(U("NcBoolean")), false, false, value::null())); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("name of datatype")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); + web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("If set the descriptor would contain all inherited elements")), nmos::fields::nc::include_inherited, value::string(U("NcBoolean")), false, false)); web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Get a single datatype descriptor")), make_nc_method_id(3, 2), U("GetDatatype"), U("NcMethodResultDatatypeDescriptor"), parameters, false)); } return methods; } - web::json::value make_nc_class_manager_events() { using web::json::value; @@ -624,6 +618,79 @@ namespace nmos return value::array(); } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor + web::json::value make_nc_receiver_monitor_properties() + { + using web::json::value; + + auto properties = value::array(); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Connection status property")), make_nc_property_id(3, 1), nmos::fields::nc::connection_status, value::string(U("NcConnectionStatus")), true, false, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Connection status message property")), make_nc_property_id(3, 2), nmos::fields::nc::connection_status_message, value::string(U("NcString")), true, true, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Payload status property")), make_nc_property_id(3, 3), nmos::fields::nc::payload_status, value::string(U("NcPayloadStatus")), true, false, false, false)); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Payload status message property")), make_nc_property_id(3, 4), nmos::fields::nc::payload_status_message, value::string(U("NcString")), true, true, false, false)); + + return properties; + } + web::json::value make_nc_receiver_monitor_methods() + { + using web::json::value; + + return value::array(); + } + web::json::value make_nc_receiver_monitor_events() + { + using web::json::value; + + return value::array(); + } + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitorprotected + web::json::value make_nc_receiver_monitor_protected_properties() + { + using web::json::value; + + auto properties = value::array(); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Indicates if signal protection is active")), make_nc_property_id(4, 1), nmos::fields::nc::signal_protection_status, value::string(U("NcBoolean")), true, false, false, false)); + + return properties; + } + web::json::value make_nc_receiver_monitor_protected_methods() + { + using web::json::value; + + return value::array(); + } + web::json::value make_nc_receiver_monitor_protected_events() + { + using web::json::value; + + return value::array(); + } + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon + web::json::value make_nc_ident_beacon_properties() + { + using web::json::value; + + auto properties = value::array(); + web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Indicator active state")), make_nc_property_id(3, 1), nmos::fields::nc::active, value::string(U("NcBoolean")), false, false, false, false)); + + return properties; + } + web::json::value make_nc_ident_beacon_methods() + { + using web::json::value; + + return value::array(); + } + web::json::value make_nc_ident_beacon_events() + { + using web::json::value; + + return value::array(); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.html web::json::value make_nc_object_class() { using web::json::value; @@ -631,6 +698,7 @@ namespace nmos return make_nc_class_descriptor(value::string(U("NcObject class descriptor")), nc_object_class_id, U("NcObject"), value::null(), make_nc_object_properties(), make_nc_object_methods(), make_nc_object_events()); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.1.html web::json::value make_nc_block_class() { using web::json::value; @@ -638,6 +706,7 @@ namespace nmos return make_nc_class_descriptor(value::string(U("NcBlock class descriptor")), nc_block_class_id, U("NcBlock"), value::null(), make_nc_block_properties(), make_nc_block_methods(), make_nc_block_events()); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.2.html web::json::value make_nc_worker_class() { using web::json::value; @@ -645,6 +714,7 @@ namespace nmos return make_nc_class_descriptor(value::string(U("NcWorker class descriptor")), nc_worker_class_id, U("NcWorker"), value::null(), make_nc_worker_properties(), make_nc_worker_methods(), make_nc_worker_events()); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.html web::json::value make_nc_manager_class() { using web::json::value; @@ -652,6 +722,7 @@ namespace nmos return make_nc_class_descriptor(value::string(U("NcManager class descriptor")), nc_manager_class_id, U("NcManager"), value::null(), make_nc_manager_properties(), make_nc_manager_methods(), make_nc_manager_events()); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.1.html web::json::value make_nc_device_manager_class() { using web::json::value; @@ -659,6 +730,7 @@ namespace nmos return make_nc_class_descriptor(value::string(U("NcDeviceManager class descriptor")), nc_device_manager_class_id, U("NcDeviceManager"), value::string(U("DeviceManager")), make_nc_device_manager_properties(), make_nc_device_manager_methods(), make_nc_device_manager_events()); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.2.html web::json::value make_nc_class_manager_class() { using web::json::value; @@ -666,65 +738,326 @@ namespace nmos return make_nc_class_descriptor(value::string(U("NcClassManager class descriptor")), nc_class_manager_class_id, U("NcClassManager"), value::string(U("ClassManager")), make_nc_class_manager_properties(), make_nc_class_manager_methods(), make_nc_class_manager_events()); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcBlockMemberDescriptor.html + web::json::value make_nc_block_member_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Role of member in its containing block")), nmos::fields::nc::role, value::string(U("NcString")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("OID of member")), nmos::fields::nc::oid, value::string(U("NcOid")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff member's OID is hardwired into device")), nmos::fields::nc::constant_oid, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Class ID")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("User label")), nmos::fields::nc::user_label, value::string(U("NcString")), true, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Containing block's OID")), nmos::fields::nc::owner, value::string(U("NcOid")), false, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Descriptor which is specific to a block member")), U("NcBlockMemberDescriptor"), fields, value::string(U("NcDescriptor"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcClassDescriptor.html + web::json::value make_nc_class_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Identity of the class")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of the class")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Role if the class has fixed role (manager classes)")), nmos::fields::nc::fixed_role, value::string(U("NcString")), true, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Property descriptors")), nmos::fields::nc::properties, value::string(U("NcPropertyDescriptor")), false, true)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Method descriptors")), nmos::fields::nc::methods, value::string(U("NcMethodDescriptor")), false, true)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Event descriptors")), nmos::fields::nc::events, value::string(U("NcEventDescriptor")), false, true)); + return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class")), U("NcClassDescriptor"), fields, value::string(U("NcDescriptor"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcClassId.html web::json::value make_nc_class_id_datatype() { using web::json::value; - return make_nc_datatype_typedef(value::string(U("Sequence of class ID fields")), U("NcClassId"), value::null(), U("NcInt32"), true); + return make_nc_datatype_typedef(value::string(U("Sequence of class ID fields")), U("NcClassId"), true, U("NcInt32")); } - web::json::value make_nc_oid_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptor.html + web::json::value make_nc_datatype_descriptor_datatype() { using web::json::value; - return make_nc_datatype_typedef(value::string(U("Object id")), U("NcOid"), value::null(), U("NcUint32"), false); + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Datatype name")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Type: Primitive, Typedef, Struct, Enum")), nmos::fields::nc::type, value::string(U("NcDatatypeType")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Base datatype descriptor")), U("NcDatatypeDescriptor"), fields, value::string(U("NcDescriptor"))); } - web::json::value make_nc_touchpoint_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorEnum.html + web::json::value make_nc_datatype_descriptor_enum_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("One item descriptor per enum option")), nmos::fields::nc::items, value::string(U("NcEnumItemDescriptor")), false, true)); + return make_nc_datatype_descriptor_struct(value::string(U("Enum datatype descriptor")), U("NcDatatypeDescriptorEnum"), fields, value::string(U("NcDatatypeDescriptor"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorPrimitive.html + web::json::value make_nc_datatype_descriptor_primitive_datatype() + { + using web::json::value; + + auto fields = value::array(); + return make_nc_datatype_descriptor_struct(value::string(U("Primitive datatype descriptor")), U("NcDatatypeDescriptorPrimitive"), fields, value::string(U("NcDatatypeDescriptor"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorStruct.html + web::json::value make_nc_datatype_descriptor_struct_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("One item descriptor per field of the struct")), nmos::fields::nc::fields, value::string(U("NcFieldDescriptor")), false, true)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of the parent type if any or null if it has no parent")), nmos::fields::nc::parent_type, value::string(U("NcName")), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Struct datatype descriptor")), U("NcDatatypeDescriptorStruct"), fields, value::string(U("NcDatatypeDescriptor"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorTypeDef.html + web::json::value make_nc_datatype_descriptor_type_def_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Original typedef datatype name")), nmos::fields::nc::parent_type, value::string(U("NcName")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff type is a typedef sequence of another type")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Type def datatype descriptor")), U("NcDatatypeDescriptorTypeDef"), fields, value::string(U("NcDatatypeDescriptor"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeType.html + web::json::value make_nc_datatype_type_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Primitive datatype")), U("Primitive"), 0)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Simple alias of another datatype")), U("Typedef"), 1)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Data structure")), U("Struct"), 2)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Enum datatype")), U("Enum"), 3)); + return make_nc_datatype_descriptor_enum(value::string(U("Datatype type")), U("NcDatatypeType"), items); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDescriptor.html + web::json::value make_nc_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional user facing description")), nmos::fields::nc::description, value::string(U("NcString")), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Base descriptor")), U("NcDescriptor"), fields, value::null()); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDeviceGenericState.html + web::json::value make_nc_device_generic_state_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Normal operation")), U("NormalOperation"), 1)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Device is initializing")), U("Initializing"), 2)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Device is performing a software or firmware update")), U("Updating"), 3)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Device is experiencing a licensing error")), U("LicensingError"), 4)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Device is experiencing an internal error")), U("InternalError"), 5)); + return make_nc_datatype_descriptor_enum(value::string(U("Device generic operational state")), U("NcDeviceGenericState"), items); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDeviceOperationalState.html + web::json::value make_nc_device_operational_state_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Context namespace")), nmos::fields::nc::context_namespace, value::string(U("NcString")), false, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Base touchpoint class")), U("NcTouchpoint"), value::null(), fields, value::null()); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Generic operational state")), nmos::fields::nc::generic_state, value::string(U("NcDeviceGenericState")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Specific device details")), nmos::fields::nc::device_specific_details, value::string(U("NcString")), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Device operational state")), U("NcDeviceOperationalState"), fields, value::null()); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcElementId.html web::json::value make_nc_element_id_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Level of the element")), nmos::fields::nc::level, value::string(U("NcUint16")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Index of the element")), nmos::fields::nc::index, value::string(U("NcUint16")), false, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Class element id which contains the level and index")), U("NcElementId"), value::null(), fields, value::null()); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Level of the element")), nmos::fields::nc::level, value::string(U("NcUint16")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Index of the element")), nmos::fields::nc::index, value::string(U("NcUint16")), false, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Class element id which contains the level and index")), U("NcElementId"), fields, value::null()); } - web::json::value make_nc_property_id_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEnumItemDescriptor.html + web::json::value make_nc_enum_item_descriptor_datatype() { using web::json::value; - return make_nc_datatype_descriptor_struct(value::string(U("Property id which contains the level and index")), U("NcPropertyId"), value::null(), value::array(), value::string(U("NcElementId"))); + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of option")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Enum item numerical value")), nmos::fields::nc::value, value::string(U("NcUint16")), false, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of an enum item")), U("NcEnumItemDescriptor"), fields, value::string(U("NcDescriptor"))); } - web::json::value make_nc_property_contraints_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEventDescriptor.html + web::json::value make_nc_event_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Event id with level and index")), nmos::fields::nc::id, value::string(U("NcEventId")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of event")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of event data's datatype")), nmos::fields::nc::event_datatype, value::string(U("NcName")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class event")), U("NcEventDescriptor"), fields, value::string(U("NcDescriptor"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEventId.html + web::json::value make_nc_event_id_datatype() + { + using web::json::value; + + return make_nc_datatype_descriptor_struct(value::string(U("Event id which contains the level and index")), U("NcEventId"), value::array(), value::string(U("NcElementId"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcFieldDescriptor.html + web::json::value make_nc_field_descriptor_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("The id of the property being constrained")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional default value")), nmos::fields::nc::default_value, value::null(), true, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Property constraints class")), U("NcPropertyConstraints"), value::null(), fields, value::null()); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of field")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of field's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff field is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff field is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a field of a struct")), U("NcFieldDescriptor"), fields, value::string(U("NcDescriptor"))); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcId.html + web::json::value make_nc_id_datatype() + { + using web::json::value; + + return make_nc_datatype_typedef(value::string(U("Identity handler")), U("NcId"), false, U("NcUint32")); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcManufacturer.html + web::json::value make_nc_manufacturer_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Manufacturer's name")), nmos::fields::nc::name, value::string(U("NcString")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("IEEE OUI or CID of manufacturer")), nmos::fields::nc::organization_id, value::string(U("NcOrganizationId")), true, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("URL of the manufacturer's website")), nmos::fields::nc::website, value::string(U("NcUri")), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Manufacturer descriptor")), U("NcManufacturer"), fields, value::null()); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodDescriptor.html + web::json::value make_nc_method_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Method id with level and index")), nmos::fields::nc::id, value::string(U("NcMethodId")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of method")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of method result's datatype")), nmos::fields::nc::result_datatype, value::string(U("NcName")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Parameter descriptors if any")), nmos::fields::nc::parameters, value::string(U("NcParameterDescriptor")), false, true)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class method")), U("NcMethodDescriptor"), fields, value::string(U("NcDescriptor"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodId.html + web::json::value make_nc_method_id_datatype() + { + using web::json::value; + + return make_nc_datatype_descriptor_struct(value::string(U("Method id which contains the level and index")), U("NcMethodId"), value::array(), value::string(U("NcElementId"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResult.html + web::json::value make_nc_method_result_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Status for the invoked method")), nmos::fields::nc::status, value::string(U("NcMethodStatus")), false, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Base result of the invoked method")), U("NcMethodResult"), fields, value::null()); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultBlockMemberDescriptors.html + web::json::value make_nc_method_result_block_member_descriptors_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Block member descriptors method result value")), nmos::fields::nc::value, value::string(U("NcBlockMemberDescriptor")), false, true)); + return make_nc_datatype_descriptor_struct(value::string(U("Method result containing block member descriptors as the value")), U("NcMethodResultBlockMemberDescriptors"), fields, value::string(U("NcMethodResult"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultClassDescriptor.html + web::json::value make_nc_method_result_class_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Class descriptor method result value")), nmos::fields::nc::value, value::string(U("NcClassDescriptor")), false, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Method result containing a class descriptor as the value")), U("NcMethodResultClassDescriptor"), fields, value::string(U("NcMethodResult"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultDatatypeDescriptor.html + web::json::value make_nc_method_result_datatype_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Datatype descriptor method result value")), nmos::fields::nc::value, value::string(U("NcDatatypeDescriptor")), false, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Method result containing a datatype descriptor as the value")), U("NcMethodResultDatatypeDescriptor"), fields, value::string(U("NcMethodResult"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultError.html + web::json::value make_nc_method_result_error_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Error message")), nmos::fields::nc::error_message, value::string(U("NcString")), false, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Error result - to be used when the method call encounters an error")), U("NcMethodResultError"), fields, value::string(U("NcMethodResult"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultId.html + web::json::value make_nc_method_result_id_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Id result value")), nmos::fields::nc::value, value::string(U("NcId")), false, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Id method result")), U("NcMethodResultId"), fields, value::string(U("NcMethodResult"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultLength.html + web::json::value make_nc_method_result_length_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Length result value")), nmos::fields::nc::value, value::string(U("NcUint32")), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Length method result")), U("NcMethodResultLength"), fields, value::string(U("NcMethodResult"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultPropertyValue.html web::json::value make_nc_method_result_property_value_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Getter method value for the associated property")), nmos::fields::nc::value, value::null(), true, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Result when invoking the getter method associated with a property")), U("NcMethodResultPropertyValue"), value::null(), fields, value::string(U("NcMethodResult"))); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Getter method value for the associated property")), nmos::fields::nc::value, value::null(), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Result when invoking the getter method associated with a property")), U("NcMethodResultPropertyValue"), fields, value::string(U("NcMethodResult"))); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodStatus.html web::json::value make_nc_method_status_datatype() { using web::json::value; @@ -748,176 +1081,189 @@ namespace nmos web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Addressed property is not implemented by the addressed object")), U("PropertyNotImplemented"), 502)); web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("The device is not ready to handle any commands")), U("NotReady"), 503)); web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Method call did not finish within the allotted time")), U("Timeout"), 504)); - return make_nc_datatype_descriptor_enum(value::string(U("Method invokation status")), U("NcMethodStatus"), value::null(), items); + return make_nc_datatype_descriptor_enum(value::string(U("Method invokation status")), U("NcMethodStatus"), items); } - web::json::value make_nc_method_result_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcName.html + web::json::value make_nc_name_datatype() { using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Status for the invoked method")), nmos::fields::nc::status, value::string(U("NcMethodStatus")), false, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Base result of the invoked method")), U("NcMethodResult"), value::null(), fields, value::null()); + return make_nc_datatype_typedef(value::string(U("Programmatically significant name, alphanumerics + underscore, no spaces")), U("NcName"), false, U("NcString")); } - web::json::value make_nc_id_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcOid.html + web::json::value make_nc_oid_datatype() { using web::json::value; - return make_nc_datatype_typedef(value::string(U("Identity handler")), U("NcId"), value::null(), U("NcUint32"), false); + return make_nc_datatype_typedef(value::string(U("Object id")), U("NcOid"), false, U("NcUint32")); } - web::json::value make_nc_method_result_id_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcOrganizationId.html + web::json::value make_nc_organization_id_datatype() { using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Id result value")), nmos::fields::nc::value, value::string(U("NcId")), false, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Id method result")), U("NcMethodResultId"), value::null(), fields, value::string(U("NcMethodResult"))); + return make_nc_datatype_typedef(value::string(U("Unique 24-bit organization id")), U("NcOrganizationId"), false, U("NcInt32")); } - web::json::value make_nc_method_result_length_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraints.html + web::json::value make_nc_parameter_constraints_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Length result value")), nmos::fields::nc::value, value::string(U("NcUint32")), true, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Length method result")), U("NcMethodResultLength"), value::null(), fields, value::string(U("NcMethodResult"))); - } - - web::json::value make_nc_property_change_type_datatype() - { - using web::json::value; - - auto items = value::array(); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Current value changed")), U("ValueChanged"), 0)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Sequence item added")), U("SequenceItemAdded"), 1)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Sequence item changed")), U("SequenceItemChanged"), 2)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Sequence item removed")), U("SequenceItemRemoved"), 3)); - return make_nc_datatype_descriptor_enum(value::string(U("Type of property change")), U("NcPropertyChangeType"), value::null(), items); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Default value")), nmos::fields::nc::default_value, value::null(), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Abstract parameter constraints class")), U("NcParameterConstraints"), fields, value::null()); } - web::json::value make_nc_property_changed_event_data_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraintsNumber.html + web::json::value make_nc_parameter_constraints_number_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("The id of the property that changed")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Information regarding the change type")), nmos::fields::nc::change_type, value::string(U("NcPropertyChangeType")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Property-type specific value")), nmos::fields::nc::value, value::null(), true, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Index of sequence item if the property is a sequence")), nmos::fields::nc::sequence_item_index, value::string(U("NcId")), true, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Payload of property-changed event")), U("NcPropertyChangedEventData"), value::null(), fields, value::null()); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional maximum")), nmos::fields::nc::maximum, value::null(), true, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional minimum")), nmos::fields::nc::minimum, value::null(), true, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional step")), nmos::fields::nc::step, value::null(), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Number parameter constraints class")), U("NcParameterConstraintsNumber"), fields, value::string(U("NcParameterConstraints"))); } - web::json::value make_nc_descriptor_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraintsString.html + web::json::value make_nc_parameter_constraints_string_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional user facing description")), nmos::fields::nc::description, value::string(U("NcString")), true, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Base descriptor")), U("NcDescriptor"), value::null(), fields, value::null()); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Maximum characters allowed")), nmos::fields::nc::max_characters, value::string(U("NcUint32")), true, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Regex pattern")), nmos::fields::nc::pattern, value::string(U("NcRegex")), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("String parameter constraints class")), U("NcParameterConstraintsString"), fields, value::string(U("NcParameterConstraints"))); } - web::json::value make_nc_block_member_descriptor_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterDescriptor.html + web::json::value make_nc_parameter_descriptor_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Role of member in its containing block")), nmos::fields::nc::role, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("OID of member")), nmos::fields::nc::oid, value::string(U("NcOid")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff member's OID is hardwired into device")), nmos::fields::nc::constant_oid, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Class ID")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("User label")), nmos::fields::nc::user_label, value::string(U("NcString")), true, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Containing block's OID")), nmos::fields::nc::owner, value::string(U("NcOid")), false, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Descriptor which is specific to a block member")), U("NcBlockMemberDescriptor"), value::null(), fields, value::string(U("NcDescriptor"))); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of parameter")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of parameter's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a method parameter")), U("NcParameterDescriptor"), fields, value::string(U("NcDescriptor"))); } - web::json::value make_nc_method_result_block_member_descriptors_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcProduct.html + web::json::value make_nc_product_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Block member descriptors method result value")), nmos::fields::nc::value, value::string(U("NcBlockMemberDescriptor")), false, true, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Method result containing block member descriptors as the value")), U("NcMethodResultBlockMemberDescriptors"), value::null(), fields, value::string(U("NcMethodResult"))); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Product name")), nmos::fields::nc::name, value::string(U("NcString")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Manufacturer's unique key to product - model number, SKU, etc")), nmos::fields::nc::key, value::string(U("NcString")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Manufacturer's product revision level code")), nmos::fields::nc::revision_level, value::string(U("NcString")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Brand name under which product is sold")), nmos::fields::nc::brand_name, value::string(U("NcString")), true, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Unique UUID of product (not product instance)")), nmos::fields::nc::uuid, value::string(U("NcUuid")), true, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Text description of product")), nmos::fields::nc::description, value::string(U("NcString")), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Product descriptor")), U("NcProduct"), fields, value::null()); } - web::json::value make_nc_version_code_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyChangeType.html + web::json::value make_nc_property_change_type_datatype() { using web::json::value; - return make_nc_datatype_typedef(value::string(U("Version code in semantic versioning format")), U("NcVersionCode"), value::null(), U("NcString"), false); + auto items = value::array(); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Current value changed")), U("ValueChanged"), 0)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Sequence item added")), U("SequenceItemAdded"), 1)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Sequence item changed")), U("SequenceItemChanged"), 2)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Sequence item removed")), U("SequenceItemRemoved"), 3)); + return make_nc_datatype_descriptor_enum(value::string(U("Type of property change")), U("NcPropertyChangeType"), items); } - web::json::value make_nc_organization_id_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyChangedEventData.html + web::json::value make_nc_property_changed_event_data_datatype() { using web::json::value; - return make_nc_datatype_typedef(value::string(U("Unique 24-bit organization id")), U("NcOrganizationId"), value::null(), U("NcInt32"), false); + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("The id of the property that changed")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Information regarding the change type")), nmos::fields::nc::change_type, value::string(U("NcPropertyChangeType")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Property-type specific value")), nmos::fields::nc::value, value::null(), true, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Index of sequence item if the property is a sequence")), nmos::fields::nc::sequence_item_index, value::string(U("NcId")), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Payload of property-changed event")), U("NcPropertyChangedEventData"), fields, value::null()); } - web::json::value make_nc_uri_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraints.html + web::json::value make_nc_property_contraints_datatype() { using web::json::value; - return make_nc_datatype_typedef(value::string(U("Uniform resource identifier")), U("NcUri"), value::null(), U("NcString"), false); + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("The id of the property being constrained")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional default value")), nmos::fields::nc::default_value, value::null(), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Property constraints class")), U("NcPropertyConstraints"), fields, value::null()); } - web::json::value make_nc_manufacturer_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraintsNumber.html + web::json::value make_nc_property_constraints_number_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Manufacturer's name")), nmos::fields::nc::name, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("IEEE OUI or CID of manufacturer")), nmos::fields::nc::organization_id, value::string(U("NcOrganizationId")), true, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("URL of the manufacturer's website")), nmos::fields::nc::website, value::string(U("NcUri")), true, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Manufacturer descriptor")), U("NcManufacturer"), value::null(), fields, value::null()); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional maximum")), nmos::fields::nc::maximum, value::null(), true, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional minimum")), nmos::fields::nc::minimum, value::null(), true, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional step")), nmos::fields::nc::step, value::null(), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Number property constraints class")), U("NcPropertyConstraintsNumber"), fields, value::string(U("NcPropertyConstraints"))); } - web::json::value make_nc_uuid_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraintsString.html + web::json::value make_nc_property_constraints_string_datatype() { using web::json::value; - return make_nc_datatype_typedef(value::string(U("UUID")), U("NcUuid"), value::null(), U("NcString"), false); + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Maximum characters allowed")), nmos::fields::nc::max_characters, value::string(U("NcUint32")), true, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Regex pattern")), nmos::fields::nc::pattern, value::string(U("NcRegex")), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("String property constraints class")), U("NcPropertyConstraintsString"), fields, value::string(U("NcPropertyConstraints"))); } - web::json::value make_nc_product_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyDescriptor.html + web::json::value make_nc_property_descriptor_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Product name")), nmos::fields::nc::name, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Manufacturer's unique key to product - model number, SKU, etc")), nmos::fields::nc::key, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Manufacturer's product revision level code")), nmos::fields::nc::revision_level, value::string(U("NcString")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Brand name under which product is sold")), nmos::fields::nc::brand_name, value::string(U("NcString")), true, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Unique UUID of product (not product instance)")), nmos::fields::nc::uuid, value::string(U("NcUuid")), true, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Text description of product")), nmos::fields::nc::description, value::string(U("NcString")), true, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Product descriptor")), U("NcProduct"), value::null(), fields, value::null()); - } - - web::json::value make_nc_device_generic_state_datatype() + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Property id with level and index")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of property")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of property's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is read-only")), nmos::fields::nc::is_read_only, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class property")), U("NcPropertyDescriptor"), fields, value::string(U("NcDescriptor"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyId.html + web::json::value make_nc_property_id_datatype() { using web::json::value; - auto items = value::array(); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Normal operation")), U("NormalOperation"), 1)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Device is initializing")), U("Initializing"), 2)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Device is performing a software or firmware update")), U("Updating"), 3)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Device is experiencing a licensing error")), U("LicensingError"), 4)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Device is experiencing an internal error")), U("InternalError"), 5)); - return make_nc_datatype_descriptor_enum(value::string(U("Device generic operational state")), U("NcDeviceGenericState"), value::null(), items); + return make_nc_datatype_descriptor_struct(value::string(U("Property id which contains the level and index")), U("NcPropertyId"), value::array(), value::string(U("NcElementId"))); } - web::json::value make_nc_device_operational_state_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcRegex.html + web::json::value make_nc_regex_datatype() { using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Generic operational state")), nmos::fields::nc::generic_state, value::string(U("NcDeviceGenericState")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Specific device details")), nmos::fields::nc::device_specific_details, value::string(U("NcString")), true, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Device operational state")), U("NcDeviceOperationalState"), value::null(), fields, value::null()); + return make_nc_datatype_typedef(value::string(U("Regex pattern")), U("NcRegex"), false, U("NcString")); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcResetCause.html web::json::value make_nc_reset_cause_datatype() { using web::json::value; @@ -929,146 +1275,133 @@ namespace nmos web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Upgrade")), U("Upgrade"), 3)); web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Controller request")), U("ControllerRequest"), 4)); web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Manual request from the front panel")), U("ManualReset"), 5)); - return make_nc_datatype_descriptor_enum(value::string(U("Reset cause enum")), U("NcResetCause"), value::null(), items); + return make_nc_datatype_descriptor_enum(value::string(U("Reset cause enum")), U("NcResetCause"), items); } - web::json::value make_nc_name_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcRolePath.html + web::json::value make_nc_role_path_datatype() { using web::json::value; - return make_nc_datatype_typedef(value::string(U("Programmatically significant name, alphanumerics + underscore, no spaces")), U("NcName"), value::null(), U("NcString"), false); + return make_nc_datatype_typedef(value::string(U("Role path")), U("NcRolePath"), true, U("NcString")); } - web::json::value make_nc_property_descriptor_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTimeInterval.html + web::json::value make_nc_time_interval_datatype() { using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Property id with level and index")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of property")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of property's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is read-only")), nmos::fields::nc::is_read_only, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class property")), U("NcPropertyDescriptor"), value::null(), fields, value::string(U("NcDescriptor"))); + return make_nc_datatype_typedef(value::string(U("Time interval described in nanoseconds")), U("NcTimeInterval"), false, U("NcInt64")); } - web::json::value make_nc_parameter_descriptor_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpoint.html + web::json::value make_nc_touchpoint_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of parameter")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of parameter's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a method parameter")), U("NcParameterDescriptor"), value::null(), fields, value::string(U("NcDescriptor"))); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Context namespace")), nmos::fields::nc::context_namespace, value::string(U("NcString")), false, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Base touchpoint class")), U("NcTouchpoint"), fields, value::null()); } - web::json::value make_nc_method_id_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointNmos.html + web::json::value make_nc_touchpoint_nmos_datatype() { using web::json::value; - return make_nc_datatype_descriptor_struct(value::string(U("Method id which contains the level and index")), U("NcMethodId"), value::null(), value::array(), value::string(U("NcElementId"))); + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Context NMOS resource")), nmos::fields::nc::resource, value::string(U("NcTouchpointResourceNmos")), false, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Touchpoint class for NMOS resources")), U("NcTouchpointNmos"), fields, value::string(U("NcTouchpoint"))); } - web::json::value make_nc_method_descriptor_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointNmosChannelMapping.html + web::json::value make_nc_touchpoint_nmos_channel_mapping_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Method id with level and index")), nmos::fields::nc::id, value::string(U("NcMethodId")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of method")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of method result's datatype")), nmos::fields::nc::result_datatype, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Parameter descriptors if any")), nmos::fields::nc::parameters, value::string(U("NcParameterDescriptor")), false, true, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class method")), U("NcMethodDescriptor"), value::null(), fields, value::string(U("NcDescriptor"))); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Context Channel Mapping resource")), nmos::fields::nc::resource, value::string(U("NcTouchpointResourceNmosChannelMapping")), false, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Touchpoint class for NMOS IS-08 resources")), U("NcTouchpointNmosChannelMapping"), fields, value::string(U("NcTouchpoint"))); } - web::json::value make_nc_event_id_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResource.html + web::json::value make_nc_touchpoint_resource_datatype() { using web::json::value; - return make_nc_datatype_descriptor_struct(value::string(U("Event id which contains the level and index")), U("NcEventId"), value::null(), value::array(), value::string(U("NcElementId"))); + auto fields = value::array(); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("The type of the resource")), nmos::fields::nc::resource_type, value::string(U("NcString")), false, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Touchpoint resource class")), U("NcTouchpointResource"), fields, value::null()); } - web::json::value make_nc_event_descriptor_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResourceNmos.html + web::json::value make_nc_touchpoint_resource_nmos_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Event id with level and index")), nmos::fields::nc::id, value::string(U("NcEventId")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of event")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of event data's datatype")), nmos::fields::nc::event_datatype, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class event")), U("NcEventDescriptor"), value::null(), fields, value::string(U("NcDescriptor"))); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("NMOS resource UUID")), nmos::fields::nc::id, value::string(U("NcUuid")), false, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Touchpoint resource class for NMOS resources")), U("NcTouchpointResourceNmos"), fields, value::string(U("NcTouchpointResource"))); } - web::json::value make_nc_class_descriptor_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResourceNmosChannelMapping.html + web::json::value make_nc_touchpoint_resource_nmos_channel_mapping_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Identity of the class")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of the class")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Role if the class has fixed role (manager classes)")), nmos::fields::nc::fixed_role, value::string(U("NcString")), true, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Property descriptors")), nmos::fields::nc::properties, value::string(U("NcPropertyDescriptor")), false, true, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Method descriptors")), nmos::fields::nc::methods, value::string(U("NcMethodDescriptor")), false, true, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Event descriptors")), nmos::fields::nc::events, value::string(U("NcEventDescriptor")), false, true, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class")), U("NcClassDescriptor"), value::null(), fields, value::string(U("NcDescriptor"))); + web::json::push_back(fields, make_nc_field_descriptor(value::string(U("IS-08 Audio Channel Mapping input or output id")), nmos::fields::nc::io_id, value::string(U("NcString")), false, false)); + return make_nc_datatype_descriptor_struct(value::string(U("Touchpoint resource class for NMOS resources")), U("NcTouchpointResourceNmosChannelMapping"), fields, value::string(U("NcTouchpointResourceNmos"))); } - web::json::value make_nc_parameter_constraints_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcUri.html + web::json::value make_nc_uri_datatype() { using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Default value")), nmos::fields::nc::default_value, value::null(), true, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Abstract parameter constraints class")), U("NcParameterConstraints"), value::null(), fields, value::null()); + return make_nc_datatype_typedef(value::string(U("Uniform resource identifier")), U("NcUri"), false, U("NcString")); } - web::json::value make_nc_datatype_type_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcUuid.html + web::json::value make_nc_uuid_datatype() { using web::json::value; - auto items = value::array(); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Primitive datatype")), U("Primitive"), 0)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Simple alias of another datatype")), U("Typedef"), 1)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Data structure")), U("Struct"), 2)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Enum datatype")), U("Enum"), 3)); - return make_nc_datatype_descriptor_enum(value::string(U("Datatype type")), U("NcDatatypeType"), value::null(), items); + return make_nc_datatype_typedef(value::string(U("UUID")), U("NcUuid"), false, U("NcString")); } - web::json::value make_nc_datatype_descriptor_datatype() + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcVersionCode.html + web::json::value make_nc_version_code_datatype() { using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Datatype name")), nmos::fields::nc::name, value::string(U("NcName")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Type: Primitive, Typedef, Struct, Enum")), nmos::fields::nc::type, value::string(U("NcDatatypeType")), false, false, value::null())); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Base datatype descriptor")), U("NcDatatypeDescriptor"), value::null(), fields, value::string(U("NcDescriptor"))); + return make_nc_datatype_typedef(value::string(U("Version code in semantic versioning format")), U("NcVersionCode"), false, U("NcString")); } - web::json::value make_nc_method_result_class_descriptor_datatype() + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncconnectionstatus + web::json::value make_nc_connection_status_datatype() { using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Class descriptor method result value")), nmos::fields::nc::value, value::string(U("NcClassDescriptor")), false, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Method result containing a class descriptor as the value")), U("NcMethodResultClassDescriptor"), value::null(), fields, value::string(U("NcMethodResult"))); + auto items = value::array(); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("This is the value when there is no receiver")), U("Undefined"), 0)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Connected to a stream")), U("Connected"), 1)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Not connected to a stream")), U("Disconnected"), 2)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("A connection error was encountered")), U("ConnectionError"), 3)); + return make_nc_datatype_descriptor_enum(value::string(U("Connection status enum data typee")), U("NcConnectionStatus"), items); } - web::json::value make_nc_method_result_datatype_descriptor_datatype() + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncpayloadstatus + web::json::value make_nc_payload_status_datatype() { using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Datatype descriptor method result value")), nmos::fields::nc::value, value::string(U("NcDatatypeDescriptor")), false, false, value::null())); - return make_nc_datatype_descriptor_struct(value::string(U("Method result containing a datatype descriptor as the value")), U("NcMethodResultDatatypeDescriptor"), value::null(), fields, value::string(U("NcMethodResult"))); + auto items = value::array(); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("This is the value when there's no connection")), U("Undefined"), 0)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Payload is being received without errors and is the correct type")), U("PayloadOK"), 1)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Payload is being received but is of an unsupported type")), U("PayloadFormatUnsupported"), 2)); + web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("A payload error was encountered")), U("PayloadError"), 3)); + return make_nc_datatype_descriptor_enum(value::string(U("Connection status enum data typee")), U("NcPayloadStatus"), items); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject @@ -1133,122 +1466,28 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state) { using web::json::value; auto data = make_nc_manager(nc_class_manager_class_id, oid, true, owner, U("ClassManager"), user_label, touchpoints, runtime_property_constraints); - // minimal control classes + // core control classes data[nmos::fields::nc::control_classes] = value::array(); auto& control_classes = data[nmos::fields::nc::control_classes]; + for (const auto& control_class : control_protocol_state.control_classes) + { + auto& ctl_class = control_class.second; + web::json::push_back(control_classes, make_nc_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.fixed_role, ctl_class.properties, ctl_class.methods, ctl_class.events)); + } - // NcObject control class - web::json::push_back(control_classes, make_nc_object_class()); - // NcBlock control class - web::json::push_back(control_classes, make_nc_block_class()); - // NcWorker control class - web::json::push_back(control_classes, make_nc_worker_class()); - // NcManager control class - web::json::push_back(control_classes, make_nc_manager_class()); - // NcDeviceManager control class - web::json::push_back(control_classes, make_nc_device_manager_class()); - // NcClassManager control class - web::json::push_back(control_classes, make_nc_class_manager_class()); - - // minimal datatypes + // core datatypes data[nmos::fields::nc::datatypes] = value::array(); auto& datatypes = data[nmos::fields::nc::datatypes]; - - // NcObject datatypes - // NcClassId - web::json::push_back(datatypes, make_nc_class_id_datatype()); - // NcOid - web::json::push_back(datatypes, make_nc_oid_datatype()); - // NcTouchpoint - web::json::push_back(datatypes, make_nc_touchpoint_datatype()); - // NcElementId - web::json::push_back(datatypes, make_nc_element_id_datatype()); - // NcPropertyId - web::json::push_back(datatypes, make_nc_property_id_datatype()); - // NcPropertyConstraints - web::json::push_back(datatypes, make_nc_property_contraints_datatype()); - // NcMethodResultPropertyValue - web::json::push_back(datatypes, make_nc_method_result_property_value_datatype()); - // NcMethodStatus - web::json::push_back(datatypes, make_nc_method_status_datatype()); - // NcMethodResult - web::json::push_back(datatypes, make_nc_method_result_datatype()); - // NcId - web::json::push_back(datatypes, make_nc_id_datatype()); - // NcMethodResultId - web::json::push_back(datatypes, make_nc_method_result_id_datatype()); - // NcMethodResultLength - web::json::push_back(datatypes, make_nc_method_result_length_datatype()); - // NcPropertyChangeType - web::json::push_back(datatypes, make_nc_property_change_type_datatype()); - // NcPropertyChangedEventData - web::json::push_back(datatypes, make_nc_property_changed_event_data_datatype()); - - // NcBlock datatypes - // NcDescriptor - web::json::push_back(datatypes, make_nc_descriptor_datatype()); - // NcBlockMemberDescriptor - web::json::push_back(datatypes, make_nc_block_member_descriptor_datatype()); - // NcMethodResultBlockMemberDescriptors - web::json::push_back(datatypes, make_nc_method_result_block_member_descriptors_datatype()); - - // NcWorker has no datatypes - - // NcManager has no datatypes - - // NcDeviceManager datatypes - // NcVersionCode - web::json::push_back(datatypes, make_nc_version_code_datatype()); - // NcOrganizationId - web::json::push_back(datatypes, make_nc_organization_id_datatype()); - // NcUri - web::json::push_back(datatypes, make_nc_uri_datatype()); - // NcManufacturer - web::json::push_back(datatypes, make_nc_manufacturer_datatype()); - // NcUuid - web::json::push_back(datatypes, make_nc_uuid_datatype()); - // NcProduct - web::json::push_back(datatypes, make_nc_product_datatype()); - // NcDeviceGenericState - web::json::push_back(datatypes, make_nc_device_generic_state_datatype()); - // NcDeviceOperationalState - web::json::push_back(datatypes, make_nc_device_operational_state_datatype()); - // NcResetCause - web::json::push_back(datatypes, make_nc_reset_cause_datatype()); - - // NcClassManager datatypes - // NcName - web::json::push_back(datatypes, make_nc_name_datatype()); - // NcPropertyDescriptor - web::json::push_back(datatypes, make_nc_property_descriptor_datatype()); - // NcMethodId - web::json::push_back(datatypes, make_nc_method_id_datatype()); - // NcParameterDescriptor - web::json::push_back(datatypes, make_nc_parameter_descriptor_datatype()); - // NcMethodDescriptor - web::json::push_back(datatypes, make_nc_method_descriptor_datatype()); - // NcEventId - web::json::push_back(datatypes, make_nc_event_id_datatype()); - // NcEventDescriptor - web::json::push_back(datatypes, make_nc_event_descriptor_datatype()); - // NcClassDescriptor - web::json::push_back(datatypes, make_nc_class_descriptor_datatype()); - // NcParameterConstraints - web::json::push_back(datatypes, make_nc_parameter_constraints_datatype()); - // NcDatatypeType - web::json::push_back(datatypes, make_nc_datatype_type_datatype()); - // NcDatatypeDescriptor - web::json::push_back(datatypes, make_nc_datatype_descriptor_datatype()); - // NcMethodResultClassDescriptor - web::json::push_back(datatypes, make_nc_method_result_class_descriptor_datatype()); - // NcMethodResultDatatypeDescriptor - web::json::push_back(datatypes, make_nc_method_result_datatype_descriptor_datatype()); + for (const auto& datatype : control_protocol_state.datatypes) + { + web::json::push_back(datatypes, datatype.second.descriptor); + } return data; } diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 999e37aff..8b1b4a892 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -1,10 +1,8 @@ #ifndef NMOS_CONTROL_PROTOCOL_RESOURCE_H #define NMOS_CONTROL_PROTOCOL_RESOURCE_H -#include #include "cpprest/json_utils.h" #include "nmos/control_protocol_class_id.h" -#include "nmos/control_protocol_state.h" // for nmos::experimental::control_classes definitions namespace web { @@ -16,6 +14,11 @@ namespace web namespace nmos { + namespace experimental + { + struct control_protocol_state; + } + namespace details { namespace nc_message_type @@ -106,41 +109,67 @@ namespace nmos { enum cause { - Unknown = 0, // 0 Unknown - Power_on = 1, // 1 Power on - InternalError = 2, // 2 Internal error - Upgrade = 3, // 3 Upgrade - Controller_request = 4, // 4 Controller request - ManualReset = 5 // 5 Manual request from the front panel + Unknown = 0, // Unknown + Power_on = 1, // Power on + InternalError = 2, // Internal error + Upgrade = 3, // Upgrade + Controller_request = 4, // Controller request + ManualReset = 5 // Manual request from the front panel + }; + } + + // NcConnectionStatus + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncconnectionstatus + namespace nc_connection_status + { + enum status + { + Undefined = 0, // This is the value when there is no receiver + Connected = 1, // Connected to a stream + Disconnected = 2, // Not connected to a stream + ConnectionError = 3 // A connection error was encountered + }; + } + + // NcPayloadStatus + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncpayloadstatus + namespace nc_payload_status + { + enum status + { + Undefined = 0, // This is the value when there's no connection. + PayloadOK = 1, // Payload is being received without errors and is the correct type + PayloadFormatUnsupported = 2, // Payload is being received but is of an unsupported type + PayloadError = 3 // A payload error was encountered }; } - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncid typedef uint32_t nc_id; - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncoid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncoid typedef uint32_t nc_oid; - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncuri + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncuri typedef utility::string_t nc_uri; - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncuuid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncuuid typedef utility::string_t nc_uuid; - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid const nc_class_id nc_object_class_id({ 1 }); const nc_class_id nc_block_class_id({ 1, 1 }); const nc_class_id nc_worker_class_id({ 1, 2 }); const nc_class_id nc_manager_class_id({ 1, 3 }); const nc_class_id nc_device_manager_class_id({ 1, 3, 1 }); const nc_class_id nc_class_manager_class_id({ 1, 3, 2 }); + const nc_class_id nc_ident_beacon_class_id({ 1, 2, 2 }); + const nc_class_id nc_receiver_monitor_class_id({ 1, 2, 3 }); + const nc_class_id nc_receiver_monitor_protected_class_id({ 1, 2, 3, 1 }); - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nctouchpoint + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nctouchpoint typedef utility::string_t nc_touch_point; - typedef std::function method; - typedef std::map methods; // method_id vs method handler - web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message); web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result); @@ -212,7 +241,7 @@ namespace nmos // description can be null // type_name can be null // constraints can be null - web::json::value make_nc_field_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); + web::json::value make_nc_field_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethoddescriptor // description can be null @@ -223,7 +252,7 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncparameterdescriptor // description can be null // type_name can be null - web::json::value make_nc_parameter_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); + web::json::value make_nc_parameter_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertydescriptor // description can be null @@ -231,96 +260,217 @@ namespace nmos // type_name can be null // constraints can be null web::json::value make_nc_property_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const web::json::value& type_name, - bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints); + bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptor // description can be null // constraints can be null - web::json::value make_nc_datatype_descriptor(const web::json::value& description, const utility::string_t& name, nc_datatype_type::type type, const web::json::value& constraints); + web::json::value make_nc_datatype_descriptor(const web::json::value& description, const utility::string_t& name, nc_datatype_type::type type, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorenum // description can be null // constraints can be null // items: sequence - web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const web::json::value& items); + web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const utility::string_t& name, const web::json::value& items, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorprimitive // description can be null // constraints can be null - web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints); + web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorstruct // description can be null // constraints can be null // fields: sequence // parent_type can be null - web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const web::json::value& fields, const web::json::value& parent_type); + web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const utility::string_t& name, const web::json::value& fields, const web::json::value& parent_type, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptortypedef // description can be null // constraints can be null - web::json::value make_nc_datatype_typedef(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints, const utility::string_t& parent_type, bool is_sequence); - - // make the core control classes proprties/methods/events + web::json::value make_nc_datatype_typedef(const web::json::value& description, const utility::string_t& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints = web::json::value::null()); + + // Control class models + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/#control-class-models-for-branch-v10-dev + // + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.html + web::json::value make_nc_object_class(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.1.html + web::json::value make_nc_block_class(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.2.html + web::json::value make_nc_worker_class(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.html + web::json::value make_nc_manager_class(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.1.html + web::json::value make_nc_device_manager_class(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.2.html + web::json::value make_nc_class_manager_class(); + + // control classes proprties/methods/events + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject web::json::value make_nc_object_properties(); web::json::value make_nc_object_methods(); web::json::value make_nc_object_events(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock web::json::value make_nc_block_properties(); web::json::value make_nc_block_methods(); web::json::value make_nc_block_events(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker web::json::value make_nc_worker_properties(); web::json::value make_nc_worker_methods(); web::json::value make_nc_worker_events(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager web::json::value make_nc_manager_properties(); web::json::value make_nc_manager_methods(); web::json::value make_nc_manager_events(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager web::json::value make_nc_device_manager_properties(); web::json::value make_nc_device_manager_methods(); web::json::value make_nc_device_manager_events(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager web::json::value make_nc_class_manager_properties(); web::json::value make_nc_class_manager_methods(); web::json::value make_nc_class_manager_events(); - - // make the core datatypes + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor + web::json::value make_nc_receiver_monitor_properties(); + web::json::value make_nc_receiver_monitor_methods(); + web::json::value make_nc_receiver_monitor_events(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitorprotected + web::json::value make_nc_receiver_monitor_protected_properties(); + web::json::value make_nc_receiver_monitor_protected_methods(); + web::json::value make_nc_receiver_monitor_protected_events(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon + web::json::value make_nc_ident_beacon_properties(); + web::json::value make_nc_ident_beacon_methods(); + web::json::value make_nc_ident_beacon_events(); + + // Datatype models + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/#datatype-models-for-branch-v10-dev + // + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcBlockMemberDescriptor.html + web::json::value make_nc_block_member_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcClassDescriptor.html + web::json::value make_nc_class_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcClassId.html web::json::value make_nc_class_id_datatype(); - web::json::value make_nc_oid_datatype(); - web::json::value make_nc_touchpoint_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptor.html + web::json::value make_nc_datatype_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorEnum.html + web::json::value make_nc_datatype_descriptor_enum_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorPrimitive.html + web::json::value make_nc_datatype_descriptor_primitive_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorStruct.html + web::json::value make_nc_datatype_descriptor_struct_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorTypeDef.html + web::json::value make_nc_datatype_descriptor_type_def_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeType.html + web::json::value make_nc_datatype_type_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDescriptor.html + web::json::value make_nc_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDeviceGenericState.html + web::json::value make_nc_device_generic_state_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDeviceOperationalState.html + web::json::value make_nc_device_operational_state_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcElementId.html web::json::value make_nc_element_id_datatype(); - web::json::value make_nc_property_id_datatype(); - web::json::value make_nc_property_contraints_datatype(); - web::json::value make_nc_method_result_property_value_datatype(); - web::json::value make_nc_method_status_datatype(); - web::json::value make_nc_method_result_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEnumItemDescriptor.html + web::json::value make_nc_enum_item_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEventDescriptor.html + web::json::value make_nc_event_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEventId.html + web::json::value make_nc_event_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcFieldDescriptor.html + web::json::value make_nc_field_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcId.html web::json::value make_nc_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcManufacturer.html + web::json::value make_nc_manufacturer_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodDescriptor.html + web::json::value make_nc_method_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodId.html + web::json::value make_nc_method_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResult.html + web::json::value make_nc_method_result_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultBlockMemberDescriptors.html + web::json::value make_nc_method_result_block_member_descriptors_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultClassDescriptor.html + web::json::value make_nc_method_result_class_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultDatatypeDescriptor.html + web::json::value make_nc_method_result_datatype_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultError.html + web::json::value make_nc_method_result_error_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultId.html web::json::value make_nc_method_result_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultLength.html web::json::value make_nc_method_result_length_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultPropertyValue.html + web::json::value make_nc_method_result_property_value_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodStatus.html + web::json::value make_nc_method_status_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcName.html + web::json::value make_nc_name_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcOid.html + web::json::value make_nc_oid_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcOrganizationId.html + web::json::value make_nc_organization_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraints.html + web::json::value make_nc_parameter_constraints_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraintsNumber.html + web::json::value make_nc_parameter_constraints_number_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraintsString.html + web::json::value make_nc_parameter_constraints_string_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterDescriptor.html + web::json::value make_nc_parameter_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcProduct.html + web::json::value make_nc_product_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyChangeType.html web::json::value make_nc_property_change_type_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyChangedEventData.html web::json::value make_nc_property_changed_event_data_datatype(); - web::json::value make_nc_descriptor_datatype(); - web::json::value make_nc_block_member_descriptor_datatype(); - web::json::value make_nc_method_result_block_member_descriptors_datatype(); - web::json::value make_nc_version_code_datatype(); - web::json::value make_nc_organization_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraints.html + web::json::value make_nc_property_contraints_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraintsNumber.html + web::json::value make_nc_property_constraints_number_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraintsString.html + web::json::value make_nc_property_constraints_string_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyDescriptor.html + web::json::value make_nc_property_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyId.html + web::json::value make_nc_property_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcRegex.html + web::json::value make_nc_regex_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcResetCause.html + web::json::value make_nc_reset_cause_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcRolePath.html + web::json::value make_nc_role_path_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTimeInterval.html + web::json::value make_nc_time_interval_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpoint.html + web::json::value make_nc_touchpoint_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointNmos.html + web::json::value make_nc_touchpoint_nmos_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointNmosChannelMapping.html + web::json::value make_nc_touchpoint_nmos_channel_mapping_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResource.html + web::json::value make_nc_touchpoint_resource_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResourceNmos.html + web::json::value make_nc_touchpoint_resource_nmos_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResourceNmosChannelMapping.html + web::json::value make_nc_touchpoint_resource_nmos_channel_mapping_datatype(); + // See // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcUri.html web::json::value make_nc_uri_datatype(); - web::json::value make_nc_manufacturer_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcUuid.html web::json::value make_nc_uuid_datatype(); - web::json::value make_nc_product_datatype(); - web::json::value make_nc_device_generic_state_datatype(); - web::json::value make_nc_device_operational_state_datatype(); - web::json::value make_nc_reset_cause_datatype(); - web::json::value make_nc_name_datatype(); - web::json::value make_nc_property_descriptor_datatype(); - web::json::value make_nc_parameter_descriptor_datatype(); - web::json::value make_nc_method_id_datatype(); - web::json::value make_nc_method_descriptor_datatype(); - web::json::value make_nc_event_id_datatype(); - web::json::value make_nc_event_descriptor_datatype(); - web::json::value make_nc_class_descriptor_datatype(); - web::json::value make_nc_parameter_constraints_datatype(); - web::json::value make_nc_datatype_type_datatype(); - web::json::value make_nc_datatype_descriptor_datatype(); - web::json::value make_nc_method_result_class_descriptor_datatype(); - web::json::value make_nc_method_result_datatype_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcVersionCode.html + web::json::value make_nc_version_code_datatype(); + + // Monitoring datatypes + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#datatypes + // + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncconnectionstatus + web::json::value make_nc_connection_status_datatype(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncpayloadstatus + web::json::value make_nc_payload_status_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); @@ -337,7 +487,7 @@ namespace nmos const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager(details::nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); + web::json::value make_nc_class_manager(details::nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state); } } diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index 6c89b6f5a..c0aa6a5f8 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -33,7 +33,7 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - nmos::resource make_class_manager(details::nc_oid oid, nmos::resource& root_block) + nmos::resource make_class_manager(details::nc_oid oid, nmos::resource& root_block, const nmos::experimental::control_protocol_state& control_protocol_state) { using web::json::value; @@ -42,7 +42,7 @@ namespace nmos const auto user_label = value::string(U("Class manager")); const auto description = value::string(U("The class manager offers access to control class and data type descriptors")); - auto data = details::make_nc_class_manager(oid, owner, user_label, value::null(), value::null()); + auto data = details::make_nc_class_manager(oid, owner, user_label, value::null(), value::null(), control_protocol_state); // add NcClassManager block_member_descriptor to root block members web::json::push_back(root_block_data[nmos::fields::nc::members], diff --git a/Development/nmos/control_protocol_resources.h b/Development/nmos/control_protocol_resources.h index ca16c8d9b..b977043cf 100644 --- a/Development/nmos/control_protocol_resources.h +++ b/Development/nmos/control_protocol_resources.h @@ -7,11 +7,16 @@ namespace nmos { + namespace experimental + { + struct control_protocol_state; + } + struct resource; nmos::resource make_device_manager(details::nc_oid oid, nmos::resource& root_block, const nmos::settings& settings); - nmos::resource make_class_manager(details::nc_oid oid, nmos::resource& root_block); + nmos::resource make_class_manager(details::nc_oid oid, nmos::resource& root_block, const nmos::experimental::control_protocol_state& control_protocol_state); nmos::resource make_root_block(); } diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 7270591a3..aceb88ef1 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -13,17 +13,28 @@ namespace nmos // setup the core control classes control_classes = { + // Control class models + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/#control-class-models-for-branch-v10-dev { details::make_nc_class_id(details::nc_object_class_id), { value::string(U("NcObject class descriptor")), details::nc_object_class_id, U("NcObject"), value::null(), details::make_nc_object_properties(), details::make_nc_object_methods(), details::make_nc_object_events() } }, { details::make_nc_class_id(details::nc_block_class_id), { value::string(U("NcBlock class descriptor")), details::nc_block_class_id, U("NcBlock"), value::null(), details::make_nc_block_properties(), details::make_nc_block_methods(), details::make_nc_block_events() } }, { details::make_nc_class_id(details::nc_worker_class_id), { value::string(U("NcWorker class descriptor")), details::nc_worker_class_id, U("NcWorker"), value::null(), details::make_nc_worker_properties(), details::make_nc_worker_methods(), details::make_nc_worker_events() } }, { details::make_nc_class_id(details::nc_manager_class_id), { value::string(U("NcManager class descriptor")), details::nc_manager_class_id, U("NcManager"), value::null(), details::make_nc_manager_properties(), details::make_nc_manager_methods(), details::make_nc_manager_events() } }, { details::make_nc_class_id(details::nc_device_manager_class_id), { value::string(U("NcDeviceManager class descriptor")), details::nc_device_manager_class_id, U("NcDeviceManager"), value::string(U("DeviceManager")), details::make_nc_device_manager_properties(), details::make_nc_device_manager_methods(), details::make_nc_device_manager_events() } }, - { details::make_nc_class_id(details::nc_class_manager_class_id), { value::string(U("NcClassManager class descriptor")), details::nc_class_manager_class_id, U("NcClassManager"), value::string(U("ClassManager")), details::make_nc_class_manager_properties(), details::make_nc_class_manager_methods(), details::make_nc_class_manager_events() } } + { details::make_nc_class_id(details::nc_class_manager_class_id), { value::string(U("NcClassManager class descriptor")), details::nc_class_manager_class_id, U("NcClassManager"), value::string(U("ClassManager")), details::make_nc_class_manager_properties(), details::make_nc_class_manager_methods(), details::make_nc_class_manager_events() } }, + // identification beacon model + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon + { details::make_nc_class_id(details::nc_ident_beacon_class_id), { value::string(U("NcIdentBeacon class descriptor")), details::nc_ident_beacon_class_id, U("NcIdentBeacon"), value::null(), details::make_nc_ident_beacon_properties(), details::make_nc_ident_beacon_methods(), details::make_nc_ident_beacon_events() } }, + // Monitoring + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor + { details::make_nc_class_id(details::nc_receiver_monitor_class_id), { value::string(U("NcReceiverMonitor class descriptor")), details::nc_receiver_monitor_class_id, U("NcReceiverMonitor"), value::null(), details::make_nc_receiver_monitor_properties(), details::make_nc_receiver_monitor_methods(), details::make_nc_receiver_monitor_events() } }, + { details::make_nc_class_id(details::nc_receiver_monitor_protected_class_id), { value::string(U("NcReceiverMonitorProtected class descriptor")), details::nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), value::null(), details::make_nc_receiver_monitor_protected_properties(), details::make_nc_receiver_monitor_protected_methods(), details::make_nc_receiver_monitor_protected_events() } } }; // setup the core datatypes datatypes = { + // Dataype models + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/#datatype-models-for-branch-v10-dev { U("NcClassId"), {details::make_nc_class_id_datatype()} }, { U("NcOid"), {details::make_nc_oid_datatype()} }, { U("NcTouchpoint"), {details::make_nc_touchpoint_datatype()} }, @@ -62,7 +73,30 @@ namespace nmos { U("NcDatatypeType"), {details::make_nc_datatype_type_datatype()} }, { U("NcDatatypeDescriptor"), {details::make_nc_datatype_descriptor_datatype()} }, { U("NcMethodResultClassDescriptor"), {details::make_nc_method_result_class_descriptor_datatype()} }, - { U("NcMethodResultDatatypeDescriptor"), {details::make_nc_method_result_datatype_descriptor_datatype()} } + { U("NcMethodResultDatatypeDescriptor"), {details::make_nc_method_result_datatype_descriptor_datatype()} }, + { U("NcMethodResultError"), {details::make_nc_method_result_error_datatype()} }, + { U("NcDatatypeDescriptorEnum"), {details::make_nc_datatype_descriptor_enum_datatype()} }, + { U("NcDatatypeDescriptorPrimitive"), {details::make_nc_datatype_descriptor_primitive_datatype()} }, + { U("NcDatatypeDescriptorStruct"), {details::make_nc_datatype_descriptor_struct_datatype()} }, + { U("NcDatatypeDescriptorTypeDef"), {details::make_nc_datatype_descriptor_type_def_datatype()} }, + { U("NcEnumItemDescriptor"), {details::make_nc_enum_item_descriptor_datatype()} }, + { U("NcFieldDescriptor"), {details::make_nc_field_descriptor_datatype()} }, + { U("NcPropertyConstraintsNumber"), {details::make_nc_property_constraints_number_datatype()} }, + { U("NcPropertyConstraintsString"), {details::make_nc_property_constraints_string_datatype()} }, + { U("NcRegex"), {details::make_nc_regex_datatype()} }, + { U("NcRolePath"), {details::make_nc_role_path_datatype()} }, + { U("NcParameterConstraintsNumber"), {details::make_nc_parameter_constraints_number_datatype()} }, + { U("NcParameterConstraintsString"), {details::make_nc_parameter_constraints_string_datatype()} }, + { U("NcTimeInterval"), {details::make_nc_time_interval_datatype()} }, + { U("NcTouchpointNmos"), {details::make_nc_touchpoint_nmos_datatype()} }, + { U("NcTouchpointNmosChannelMapping"), {details::make_nc_touchpoint_nmos_channel_mapping_datatype()} }, + { U("NcTouchpointResource"), {details::make_nc_touchpoint_resource_datatype()} }, + { U("NcTouchpointResourceNmos"), {details::make_nc_touchpoint_resource_nmos_datatype()} }, + { U("NcTouchpointResourceNmosChannelMapping"), {details::make_nc_touchpoint_resource_nmos_channel_mapping_datatype()} }, + // Monitoring + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#datatypes + { U("NcConnectionStatus"), {details::make_nc_connection_status_datatype()} }, + { U("NcPayloadStatus"), {details::make_nc_payload_status_datatype()} } }; } } diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index 0dfde5990..c15e2fd65 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -5,6 +5,7 @@ #include "cpprest/json_utils.h" #include "nmos/control_protocol_class_id.h" // for nmos::details::nc_class_id definitions #include "nmos/mutex.h" +#include "nmos/resources.h" namespace nmos { @@ -20,27 +21,6 @@ namespace nmos web::json::value properties; // array of nc_property_descriptor web::json::value methods; // array of nc_method_descriptor web::json::value events; // array of nc_event_descriptor - - //control_class(details::nc_class_id class_id, utility::string_t name, web::json::value fixed_role, web::json::value properties, web::json::value methods, web::json::value events) - // : description(web::json::value::null()) - // , class_id(std::move(class_id)) - // , name(std::move(name)) - // , fixed_role(std::move(fixed_role)) - // , properties(std::move(properties)) - // , methods(std::move(methods)) - // , events(std::move(events)) - //{} - - //control_class(const utility::string_t& description, details::nc_class_id class_id, utility::string_t name, web::json::value fixed_role, web::json::value properties, web::json::value methods, web::json::value events) - // : description(web::json::value::string(description)) - // , class_id(std::move(class_id)) - // , name(std::move(name)) - // , fixed_role(std::move(fixed_role)) - // , properties(std::move(properties)) - // , methods(std::move(methods)) - // , events(std::move(events)) - //{} - }; struct datatype // NcDatatypeDescriptorEnum/NcDatatypeDescriptorPrimitive/NcDatatypeDescriptorStruct/NcDatatypeDescriptorTypeDef @@ -53,6 +33,10 @@ namespace nmos // nc_name vs datatype typedef std::map datatypes; + // methods defnitions + typedef std::function method; + typedef std::map methods; // method_id vs method handler + struct control_protocol_state { // mutex to be used to protect the members from simultaneous access by multiple threads diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index c51b90655..8e5f31752 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -5,6 +5,7 @@ #include "cpprest/regex_utils.h" #include "nmos/api_utils.h" #include "nmos/control_protocol_resources.h" +#include "nmos/control_protocol_state.h" #include "nmos/control_protocol_utils.h" #include "nmos/is12_versions.h" #include "nmos/json_schema.h" @@ -46,6 +47,597 @@ namespace nmos controlprotocol_validator().validate(request_data, experimental::make_controlprotocolapi_subscription_message_schema_uri(version)); } + // hmm, change property to struct + web::json::value find_property(const web::json::value& property_id, const nc_class_id& class_id_, const nmos::experimental::control_classes& control_classes) + { + using web::json::value; + + auto class_id = class_id_; + + while (!class_id.empty()) + { + auto class_found = control_classes.find(make_nc_class_id(class_id)); + if (control_classes.end() != class_found) + { + auto& properties = class_found->second.properties.as_array(); + for (const auto& property : properties) + { + if (property_id == nmos::fields::nc::id(property)) + { + return property; + } + } + } + class_id.pop_back(); + } + + return value::null(); + }; + + // hmm, change method_id to struct, and bring in method handlers via the control_classes + nmos::experimental::method find_method(const web::json::value& method_id, const nc_class_id& class_id_, const nmos::experimental::control_classes& control_classes) + { + using web::json::value; + using web::json::value_of; + + // NcObject methods implementation + // Get property value + const auto get = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + // where arguments is the property id = (level, index) + const auto& property_id = nmos::fields::nc::id(arguments); + + // find the relevant nc_property_descriptor + const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); + if (!property.is_null()) + { + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(property))); + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do Get"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + }; + // Set property value + const auto set = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& val = nmos::fields::nc::value(arguments); + + // find the relevant nc_property_descriptor + const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); + if (!property.is_null()) + { + if (nmos::fields::nc::is_read_only(property)) + { + return details::make_control_protocol_response(handle, { details::nc_method_status::read_only }); + } + + if ((val.is_null() && !nmos::fields::nc::is_nullable(property)) + || (val.is_array() && !nmos::fields::nc::is_sequence(property))) + { + return details::make_control_protocol_response(handle, { details::nc_method_status::parameter_error }); + } + + resources.modify(resource, [&](nmos::resource& resource) + { + resource.data[nmos::fields::nc::name(property)] = val; + + resource.updated = strictly_increasing_update(resources); + }); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << " to do Set"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + }; + // Get sequence item + const auto get_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& index = nmos::fields::nc::index(arguments); + + // find the relevant nc_property_descriptor + const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); + if (!property.is_null()) + { + if (!nmos::fields::nc::is_sequence(property)) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } + + auto& data = resource->data.at(nmos::fields::nc::name(property)); + + if (!data.is_null() && data.as_array().size() > (size_t)index) + { + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, data.at(index)); + } + + // out of bound + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do GetSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do GetSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + }; + // Set sequence item + const auto set_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& index = nmos::fields::nc::index(arguments); + const auto& val = nmos::fields::nc::value(arguments); + + // find the relevant nc_property_descriptor + const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); + if (!property.is_null()) + { + if (!nmos::fields::nc::is_sequence(property)) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do SetSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } + + auto& data = resource->data.at(nmos::fields::nc::name(property)); + + if (!data.is_null() && data.as_array().size() > (size_t)index) + { + resources.modify(resource, [&](nmos::resource& resource) + { + resource.data[nmos::fields::nc::name(property)][index] = val; + + resource.updated = strictly_increasing_update(resources); + }); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); + } + + // out of bound + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do SetSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do SetSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + }; + // Add item to sequence + const auto add_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& val = nmos::fields::nc::value(arguments); + + // find the relevant nc_property_descriptor + const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); + if (!property.is_null()) + { + if (!nmos::fields::nc::is_sequence(property)) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do AddSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } + + auto& data = resource->data.at(nmos::fields::nc::name(property)); + + resources.modify(resource, [&](nmos::resource& resource) + { + auto& sequence = resource.data[nmos::fields::nc::name(property)]; + if (data.is_null()) { sequence = value::array(); } + web::json::push_back(sequence, val); + + resource.updated = strictly_increasing_update(resources); + }); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, uint32_t(data.as_array().size() - 1)); + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do AddSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + }; + // Delete sequence item + const auto remove_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& index = nmos::fields::nc::index(arguments); + + // find the relevant nc_property_descriptor + const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); + if (!property.is_null()) + { + if (!nmos::fields::nc::is_sequence(property)) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do RemoveSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } + + auto& data = resource->data.at(nmos::fields::nc::name(property)); + + if (!data.is_null() && data.as_array().size() > (size_t)index) + { + resources.modify(resource, [&](nmos::resource& resource) + { + auto& sequence = resource.data[nmos::fields::nc::name(property)].as_array(); + sequence.erase(index); + + resource.updated = strictly_increasing_update(resources); + }); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); + } + + // out of bound + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do RemoveSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do RemoveSequenceItem"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + }; + // Get sequence length + const auto get_sequence_length = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + const auto& property_id = nmos::fields::nc::id(arguments); + + // find the relevant nc_property_descriptor + const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); + if (!property.is_null()) + { + if (!nmos::fields::nc::is_sequence(property)) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceLength"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } + + auto& data = resource->data.at(nmos::fields::nc::name(property)); + + if (nmos::fields::nc::is_nullable(property)) + { + // can be null + if (data.is_null()) + { + // null + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, value::null()); + } + } + else + { + // cannot be null + if (data.is_null()) + { + // null + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << " is a null sequence to do GetSequenceLength"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + } + } + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, uint32_t(data.as_array().size())); + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << " to do GetSequenceLength"; + return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + }; + + // NcBlock methods implementation + // Gets descriptors of members of the block + const auto get_member_descriptors = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + const auto& recurse = nmos::fields::nc::recurse(arguments); // If recurse is set to true, nested members is to be retrieved + + auto descriptors = value::array(); + nmos::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); + + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptors); + }; + // Finds member(s) by path + const auto find_members_by_path = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + // Relative path to search for (MUST not include the role of the block targeted by oid) + const auto& path = nmos::fields::nc::path(arguments); + + if (0 == path.size()) + { + // empty path + return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty path to do FindMembersByPath")); + } + + auto nc_block_member_descriptors = value::array(); + + for (const auto& role : path) + { + // look for the role in members + if (resource->data.has_field(nmos::fields::nc::members)) + { + auto& members = nmos::fields::nc::members(resource->data); + auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& nc_block_member_descriptor) + { + return role.as_string() == nmos::fields::nc::role(nc_block_member_descriptor); + }); + + if (members.end() != member_found) + { + web::json::push_back(nc_block_member_descriptors, *member_found); + + // use oid to look for the next resource + resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(*member_found)))); + } + else + { + // no role + utility::stringstream_t ss; + ss << U("role: ") << role.as_string() << U(" not found to do FindMembersByPath"); + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + } + } + else + { + // no members + return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, U("no members to do FindMembersByPath")); + } + } + + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, nc_block_member_descriptors); + }; + // Finds members with given role name or fragment + const auto find_members_by_role = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + { + const auto& role = nmos::fields::nc::role(arguments); // Role text to search for + const auto& case_sensitive = nmos::fields::nc::case_sensitive(arguments); // Signals if the comparison should be case sensitive + const auto& match_whole_string = nmos::fields::nc::match_whole_string(arguments); // TRUE to only return exact matches + const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks + + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + if (role.empty()) + { + // empty role + return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty role to do FindMembersByRole")); + } + + auto descriptors = value::array(); + nmos::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); + + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptors); + }; + // Finds members with given class id + const auto find_members_by_class_id = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + { + const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for + const auto& include_derived = nmos::fields::nc::include_derived(arguments); // If TRUE it will also include derived class descriptors + const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks + + if (class_id.empty()) + { + // empty class_id + return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty classId to do FindMembersByClassId")); + } + + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto descriptors = value::array(); + nmos::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); + + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptors); + }; + + // NcClassManager methods implementation + // Get a single class descriptor + const auto get_control_class = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + { + const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for + const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements + + if (class_id.empty()) + { + // empty class_id + return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty classId to do GetControlClass")); + } + + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto class_found = control_classes.find(make_nc_class_id(class_id)); + + if (control_classes.end() != class_found) + { + auto id = class_id; + + auto description = class_found->second.description; + auto name = class_found->second.name; + auto fixed_role = class_found->second.fixed_role; + auto properties = class_found->second.properties; + auto methods = class_found->second.methods; + auto events = class_found->second.events; + + id.pop_back(); + + if (include_inherited) + { + while (!id.empty()) + { + auto found = control_classes.find(make_nc_class_id(id)); + if (control_classes.end() != found) + { + for (const auto& property : found->second.properties.as_array()) { web::json::push_back(properties, property); } + for (const auto& method : found->second.methods.as_array()) { web::json::push_back(methods, method); } + for (const auto& event : found->second.events.as_array()) { web::json::push_back(events, event); } + } + id.pop_back(); + } + } + auto descriptor = details::make_nc_class_descriptor(description, class_id, name, fixed_role, properties, methods, events); + + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptor); + } + + return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("classId not found")); + }; + // Get a single datatype descriptor + const auto get_datatype = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + { + const auto& name = nmos::fields::nc::name(arguments); // name of datatype + const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements + + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + if (name.empty()) + { + // empty name + return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty name to do GetDatatype")); + } + + auto datatype_found = datatypes.find(name); + + if (datatypes.end() != datatype_found) + { + auto descriptor = datatype_found->second.descriptor; + + if (include_inherited) + { + const auto& type = nmos::fields::nc::type(descriptor); + if (details::nc_datatype_type::Struct == type) + { + auto descriptor_ = descriptor; + + for (;;) + { + const auto& parent_type = descriptor_.at(nmos::fields::nc::parent_type); + if (!parent_type.is_null()) + { + auto datatype_found_ = datatypes.find(parent_type.as_string()); + if (datatypes.end() != datatype_found_) + { + descriptor_ = datatype_found_->second.descriptor; + const auto& fields = nmos::fields::nc::fields(descriptor_); + for (const auto& field : fields) + { + web::json::push_back(descriptor.at(nmos::fields::nc::fields), field); + } + } + } + else + { + break; + } + } + } + } + + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptor); + } + + return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("name not found")); + }; + + // method handlers for the different classes + nmos::experimental::methods nc_object_method_handlers; // method_id vs NcObject method_handler + nmos::experimental::methods nc_block_method_handlers; // method_id vs NcBlock method_handler + nmos::experimental::methods nc_worker_method_handlers; // method_id vs NcWorker method_handler + nmos::experimental::methods nc_manager_method_handlers; // method_id vs NcManager method_handler + nmos::experimental::methods nc_device_manager_method_handlers; // method_id vs NcDeviceManager method_handler + nmos::experimental::methods nc_class_manager_method_handlers; // method_id vs NcClassManager method_handler + + // NcObject methods + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject + nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 1 } })] = get; + nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 2 } })] = set; + nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 3 } })] = get_sequence_item; + nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 4 } })] = set_sequence_item; + nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 5 } })] = add_sequence_item; + nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 6 } })] = remove_sequence_item; + nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 7 } })] = get_sequence_length; + + // NcBlock methods + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 1 } })] = get_member_descriptors; + nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 2 } })] = find_members_by_path; + nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 3 } })] = find_members_by_role; + nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 4 } })] = find_members_by_class_id; + + // NcWorker has no extended method + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker + + // NcManager has no extended method + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager + + // NcDeviceManger has no extended method + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + + // NcClassManager methods + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + nc_class_manager_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 1 } })] = get_control_class; + nc_class_manager_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 2 } })] = get_datatype; + + // class id vs method handlers + // hmm, todo, custom class and assoicated methods will need to be inserted within follwoing table! + const std::map methods = + { + { details::make_nc_class_id(details::nc_object_class_id), nc_object_method_handlers }, + { details::make_nc_class_id(details::nc_block_class_id), nc_block_method_handlers }, + { details::make_nc_class_id(details::nc_class_manager_class_id), nc_class_manager_method_handlers } + }; + + auto class_id = class_id_; + + while (!class_id.empty()) + { + auto subset_methods_found = methods.find(make_nc_class_id(class_id)); + + if (methods.end() != subset_methods_found) + { + auto& subset_methods = subset_methods_found->second; + auto method_found = subset_methods.find(method_id); + if (subset_methods.end() != method_found) + { + return method_found->second; + } + } + class_id.pop_back(); + } + + return NULL; + } + + /* std::pair create_properties_methods(nmos::node_model& model, const nc_class_id& class_id_, const nmos::experimental::control_classes& control_classes) { using web::json::value; @@ -55,7 +647,7 @@ namespace nmos // NcObject methods implementation // Get property value - const auto get = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto get = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -67,13 +659,10 @@ namespace nmos const auto& property_id = nmos::fields::nc::id(arguments); // find the relevant nc_property_descriptor - auto property_found = std::find_if(properties.begin(), properties.end(), [property_id](const web::json::value& property) - { - return property_id == nmos::fields::nc::id(property); - }); - if (properties.end() != property_found) + const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); + if (!property.is_null()) { - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(*property_found))); + return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(property))); } // unknown property @@ -88,7 +677,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Set property value - const auto set = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto set = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -100,26 +689,23 @@ namespace nmos const auto& val = nmos::fields::nc::value(arguments); // find the relevant nc_property_descriptor - auto property_found = std::find_if(properties.begin(), properties.end(), [property_id](const web::json::value& property) - { - return property_id == nmos::fields::nc::id(property); - }); - if (properties.end() != property_found) + const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); + if (!property.is_null()) { - if (nmos::fields::nc::is_read_only(*property_found)) + if (nmos::fields::nc::is_read_only(property)) { return details::make_control_protocol_response(handle, { details::nc_method_status::read_only }); } - if ((val.is_null() && !nmos::fields::nc::is_nullable(*property_found)) - || (val.is_array() && !nmos::fields::nc::is_sequence(*property_found))) + if ((val.is_null() && !nmos::fields::nc::is_nullable(property)) + || (val.is_array() && !nmos::fields::nc::is_sequence(property))) { return details::make_control_protocol_response(handle, { details::nc_method_status::parameter_error }); } resources.modify(resource, [&](nmos::resource& resource) { - resource.data[nmos::fields::nc::name(*property_found)] = val; + resource.data[nmos::fields::nc::name(property)] = val; resource.updated = strictly_increasing_update(resources); }); @@ -138,7 +724,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Get sequence item - const auto get_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto get_sequence_item = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -150,13 +736,10 @@ namespace nmos const auto& index = nmos::fields::nc::index(arguments); // find the relevant nc_property_descriptor - auto property_found = std::find_if(properties.begin(), properties.end(), [property_id](const web::json::value& property) + const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); + if (!property.is_null()) { - return property_id == nmos::fields::nc::id(property); - }); - if (properties.end() != property_found) - { - if (!nmos::fields::nc::is_sequence(*property_found)) + if (!nmos::fields::nc::is_sequence(property)) { // property is not a sequence utility::stringstream_t ss; @@ -164,7 +747,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); } - auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); + auto& data = resource->data.at(nmos::fields::nc::name(property)); if (!data.is_null() && data.as_array().size() > (size_t)index) { @@ -189,7 +772,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Set sequence item - const auto set_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto set_sequence_item = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -202,13 +785,10 @@ namespace nmos const auto& val = nmos::fields::nc::value(arguments); // find the relevant nc_property_descriptor - auto property_found = std::find_if(properties.begin(), properties.end(), [property_id](const web::json::value& property) - { - return property_id == nmos::fields::nc::id(property); - }); - if (properties.end() != property_found) + const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); + if (!property.is_null()) { - if (!nmos::fields::nc::is_sequence(*property_found)) + if (!nmos::fields::nc::is_sequence(property)) { // property is not a sequence utility::stringstream_t ss; @@ -216,13 +796,13 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); } - auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); + auto& data = resource->data.at(nmos::fields::nc::name(property)); if (!data.is_null() && data.as_array().size() > (size_t)index) { resources.modify(resource, [&](nmos::resource& resource) { - resource.data[nmos::fields::nc::name(*property_found)][index] = val; + resource.data[nmos::fields::nc::name(property)][index] = val; resource.updated = strictly_increasing_update(resources); }); @@ -247,7 +827,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Add item to sequence - const auto add_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto add_sequence_item = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -259,13 +839,10 @@ namespace nmos const auto& val = nmos::fields::nc::value(arguments); // find the relevant nc_property_descriptor - auto property_found = std::find_if(properties.begin(), properties.end(), [property_id](const web::json::value& property) - { - return property_id == nmos::fields::nc::id(property); - }); - if (properties.end() != property_found) + const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); + if (!property.is_null()) { - if (!nmos::fields::nc::is_sequence(*property_found)) + if (!nmos::fields::nc::is_sequence(property)) { // property is not a sequence utility::stringstream_t ss; @@ -273,11 +850,11 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); } - auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); + auto& data = resource->data.at(nmos::fields::nc::name(property)); resources.modify(resource, [&](nmos::resource& resource) { - auto& sequence = resource.data[nmos::fields::nc::name(*property_found)]; + auto& sequence = resource.data[nmos::fields::nc::name(property)]; if (data.is_null()) { sequence = value::array(); } web::json::push_back(sequence, val); @@ -298,7 +875,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Delete sequence item - const auto remove_sequence_item = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto remove_sequence_item = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -310,13 +887,10 @@ namespace nmos const auto& index = nmos::fields::nc::index(arguments); // find the relevant nc_property_descriptor - auto property_found = std::find_if(properties.begin(), properties.end(), [property_id](const web::json::value& property) + const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); + if (!property.is_null()) { - return property_id == nmos::fields::nc::id(property); - }); - if (properties.end() != property_found) - { - if (!nmos::fields::nc::is_sequence(*property_found)) + if (!nmos::fields::nc::is_sequence(property)) { // property is not a sequence utility::stringstream_t ss; @@ -324,13 +898,13 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); } - auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); + auto& data = resource->data.at(nmos::fields::nc::name(property)); if (!data.is_null() && data.as_array().size() > (size_t)index) { resources.modify(resource, [&](nmos::resource& resource) { - auto& sequence = resource.data[nmos::fields::nc::name(*property_found)].as_array(); + auto& sequence = resource.data[nmos::fields::nc::name(property)].as_array(); sequence.erase(index); resource.updated = strictly_increasing_update(resources); @@ -356,7 +930,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Get sequence length - const auto get_sequence_length = [&model](const web::json::array& properties, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto get_sequence_length = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -367,14 +941,10 @@ namespace nmos const auto& property_id = nmos::fields::nc::id(arguments); // find the relevant nc_property_descriptor - auto property_found = std::find_if(properties.begin(), properties.end(), [property_id](const web::json::value& property) - { - return property_id == nmos::fields::nc::id(property); - }); - - if (property_found != properties.end()) + const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); + if (!property.is_null()) { - if (!nmos::fields::nc::is_sequence(*property_found)) + if (!nmos::fields::nc::is_sequence(property)) { // property is not a sequence utility::stringstream_t ss; @@ -382,9 +952,9 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); } - auto& data = resource->data.at(nmos::fields::nc::name(*property_found)); + auto& data = resource->data.at(nmos::fields::nc::name(property)); - if (nmos::fields::nc::is_nullable(*property_found)) + if (nmos::fields::nc::is_nullable(property)) { // can be null if (data.is_null()) @@ -421,7 +991,7 @@ namespace nmos // NcBlock methods implementation // Gets descriptors of members of the block - const auto get_member_descriptors = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto get_member_descriptors = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -443,7 +1013,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Finds member(s) by path - const auto find_members_by_path = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto find_members_by_path = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -504,7 +1074,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Finds members with given role name or fragment - const auto find_members_by_role = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto find_members_by_role = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { const auto& role = nmos::fields::nc::role(arguments); // Role text to search for const auto& case_sensitive = nmos::fields::nc::case_sensitive(arguments); // Signals if the comparison should be case sensitive @@ -536,7 +1106,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Finds members with given class id - const auto find_members_by_class_id = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto find_members_by_class_id = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for const auto& include_derived = nmos::fields::nc::include_derived(arguments); // If TRUE it will also include derived class descriptors @@ -569,7 +1139,7 @@ namespace nmos // NcClassManager methods implementation // Get a single class descriptor - const auto get_control_class = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto get_control_class = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements @@ -630,7 +1200,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); }; // Get a single datatype descriptor - const auto get_datatype = [&model](const web::json::array&, int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto get_datatype = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) { const auto& name = nmos::fields::nc::name(arguments); // name of datatype const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements @@ -793,6 +1363,7 @@ namespace nmos return { properties, methods }; } + */ } // IS-12 Control Protocol WebSocket API @@ -994,18 +1565,14 @@ namespace nmos auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != resource) { - // create the combined properties and method handlers based on class_id auto class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); - auto properties_methods = details::create_properties_methods(model, class_id, get_control_protocol_classes()); - auto& properties = properties_methods.first; - auto& methods = properties_methods.second; // find the relevent method handler to execute - auto method_found = methods.find(method_id); - if (method_found != methods.end()) + auto method = details::find_method(method_id, class_id, get_control_protocol_classes()); + if (method) { // execute the relevant method handler, then accumulating up their response to reponses - web::json::push_back(responses, method_found->second(properties.as_array(), handle, oid, arguments, get_control_protocol_classes(), get_control_protocol_datatypes())); + web::json::push_back(responses, method(resources, resource, handle, arguments, get_control_protocol_classes(), get_control_protocol_datatypes())); } else { diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index a18242e7c..df831eaec 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -316,6 +316,20 @@ namespace nmos const web::json::field_as_integer change_type{ U("changeType") }; // NcPropertyChangeType const web::json::field_as_integer sequence_item_index{ U("sequenceItemIndex") }; // NcId, can be null const web::json::field_as_value property_id{ U("propertyId") }; + const web::json::field_as_integer maximum{ U("maximum") }; + const web::json::field_as_integer minimum{ U("minimum") }; + const web::json::field_as_integer step{ U("step") }; + const web::json::field_as_integer max_characters{ U("maxCharacters") }; + const web::json::field_as_string pattern{ U("pattern") }; + const web::json::field_as_value resource{ U("resource") }; + const web::json::field_as_string resource_type{ U("resourceType") }; + const web::json::field_as_string io_id{ U("ioId") }; + const web::json::field_as_integer connection_status{ U("connectionStatus") }; // NcConnectionStatus + const web::json::field_as_string connection_status_message{ U("connectionStatusMessage") }; + const web::json::field_as_integer payload_status{ U("payloadStatus") }; // NcPayloadStatus + const web::json::field_as_string payload_status_message{ U("payloadStatusMessage") }; + const web::json::field_as_bool signal_protection_status{ U("signalProtectionStatus") }; + const web::json::field_as_bool active{ U("active") }; } // NMOS Parameter Registers From f7dacb2a21abd60d9e02bb5ea984685fde5f695f Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 18 Aug 2023 18:50:59 +0100 Subject: [PATCH 025/250] Use control_protocol_ws_port to construct request URLs for the Control Protocol websocket, or negative to disable the control protocol features --- Development/nmos-cpp-node/config.json | 1 + Development/nmos-cpp-node/main.cpp | 7 ++++-- .../nmos-cpp-node/node_implementation.cpp | 23 +++++++++++-------- Development/nmos/node_server.cpp | 23 +++++++++++++------ Development/nmos/settings.h | 1 + 5 files changed, 36 insertions(+), 19 deletions(-) diff --git a/Development/nmos-cpp-node/config.json b/Development/nmos-cpp-node/config.json index 63625c42e..397da858f 100644 --- a/Development/nmos-cpp-node/config.json +++ b/Development/nmos-cpp-node/config.json @@ -137,6 +137,7 @@ //"channelmapping_port": 3215, // system_port [node]: used to construct request URLs for the System API (if not discovered via DNS-SD) //"system_port": 10641, + // control_protocol_ws_port [node]: used to construct request URLs for the Control Protocol websocket, or negative to disable the control protocol features //"control_protocol_ws_port": 3218, // listen_backlog [registry, node]: the maximum length of the queue of pending connections, or zero for the implementation default (the implementation may not honour this value) diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index 5b17eebb5..5dd65c38e 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -110,8 +110,11 @@ int main(int argc, char* argv[]) #endif nmos::experimental::control_protocol_state control_protocol_state; - node_implementation.on_get_control_classes(nmos::make_get_control_protocol_classes_handler(control_protocol_state, gate)); - node_implementation.on_get_control_datatypes(nmos::make_get_control_protocol_datatypes_handler(control_protocol_state, gate)); + if (0 <= nmos::fields::control_protocol_ws_port(node_model.settings)) + { + node_implementation.on_get_control_classes(nmos::make_get_control_protocol_classes_handler(control_protocol_state, gate)); + node_implementation.on_get_control_datatypes(nmos::make_get_control_protocol_datatypes_handler(control_protocol_state, gate)); + } // Set up the node server diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index d232d3bf3..2d4774f13 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -897,16 +897,19 @@ void node_implementation_init(nmos::node_model& model, const nmos::experimental: if (!insert_resource_after(delay_millis, model.channelmapping_resources, std::move(channelmapping_output), gate)) throw node_implementation_init_exception(); } - // example root block - auto root_block = nmos::make_root_block(); - // example device manager - auto device_manager = nmos::make_device_manager(2, root_block, model.settings); - if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(device_manager), gate)) throw node_implementation_init_exception(); - // example class manager - auto class_manager = nmos::make_class_manager(3, root_block, control_protocol_state); - if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(class_manager), gate)) throw node_implementation_init_exception(); - // insert root block to model - if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(root_block), gate)) throw node_implementation_init_exception(); + if (0 <= nmos::fields::control_protocol_ws_port(model.settings)) + { + // example root block + auto root_block = nmos::make_root_block(); + // example device manager + auto device_manager = nmos::make_device_manager(2, root_block, model.settings); + if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(device_manager), gate)) throw node_implementation_init_exception(); + // example class manager + auto class_manager = nmos::make_class_manager(3, root_block, control_protocol_state); + if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(class_manager), gate)) throw node_implementation_init_exception(); + // insert root block to model + if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(root_block), gate)) throw node_implementation_init_exception(); + } } void node_implementation_run(nmos::node_model& model, slog::base_gate& gate) diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index d163fdea8..cf4c15054 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -69,10 +69,14 @@ namespace nmos events_ws_api.first = nmos::make_events_ws_api(node_model, events_ws_api.second, gate); // can't share a port between the events ws and the control protocol ws + const auto& control_protocol_enabled = (0 <= nmos::fields::control_protocol_ws_port(node_model.settings)); const auto& control_protocol_ws_port = nmos::fields::control_protocol_ws_port(node_model.settings); - if (control_protocol_ws_port == events_ws_port) throw std::runtime_error("Same port used for events and control protocol websockets are not supported"); - auto& control_protocol_ws_api = node_server.ws_handlers[{ {}, control_protocol_ws_port }]; - control_protocol_ws_api.first = nmos::make_control_protocol_ws_api(node_model, control_protocol_ws_api.second, node_implementation.get_control_protocol_classes, node_implementation.get_control_protocol_datatypes, gate); + if (control_protocol_enabled) + { + if (control_protocol_ws_port == events_ws_port) throw std::runtime_error("Same port used for events and control protocol websockets are not supported"); + auto& control_protocol_ws_api = node_server.ws_handlers[{ {}, control_protocol_ws_port }]; + control_protocol_ws_api.first = nmos::make_control_protocol_ws_api(node_model, control_protocol_ws_api.second, node_implementation.get_control_protocol_classes, node_implementation.get_control_protocol_datatypes, gate); + } // Set up the listeners for each HTTP API port @@ -110,7 +114,7 @@ namespace nmos else { ++event_ws_pos; } } - if (!found_control_protocol_ws) + if (control_protocol_enabled && !found_control_protocol_ws) { if (ws_handler.first.second == control_protocol_ws_port) { found_control_protocol_ws = true; } else { ++control_protocol_ws_pos; } @@ -118,7 +122,6 @@ namespace nmos } auto& events_ws_listener = node_server.ws_listeners.at(event_ws_pos); - auto& control_protocol_ws_listener = node_server.ws_listeners.at(control_protocol_ws_pos); // Set up node operation (including the DNS-SD advertisements) @@ -133,8 +136,7 @@ namespace nmos [&] { nmos::send_events_ws_messages_thread(events_ws_listener, node_model, events_ws_api.second, gate); }, [&] { nmos::erase_expired_events_resources_thread(node_model, gate); }, [&, resolve_auto, set_transportfile, connection_activated] { nmos::connection_activation_thread(node_model, resolve_auto, set_transportfile, connection_activated, gate); }, - [&, channelmapping_activated] { nmos::channelmapping_activation_thread(node_model, channelmapping_activated, gate); }, - [&] { nmos::send_control_protocol_ws_messages_thread(control_protocol_ws_listener, node_model, control_protocol_ws_api.second, gate); } + [&, channelmapping_activated] { nmos::channelmapping_activation_thread(node_model, channelmapping_activated, gate); } }); auto system_changed = node_implementation.system_changed; @@ -143,6 +145,13 @@ namespace nmos node_server.thread_functions.push_back([&, load_ca_certificates, system_changed] { nmos::node_system_behaviour_thread(node_model, load_ca_certificates, system_changed, gate); }); } + if (control_protocol_enabled) + { + auto& control_protocol_ws_listener = node_server.ws_listeners.at(control_protocol_ws_pos); + auto& control_protocol_ws_api = node_server.ws_handlers.at({ {}, control_protocol_ws_port }); + node_server.thread_functions.push_back([&] { nmos::send_control_protocol_ws_messages_thread(control_protocol_ws_listener, node_model, control_protocol_ws_api.second, gate); }); + } + return node_server; } diff --git a/Development/nmos/settings.h b/Development/nmos/settings.h index e5e46ed0f..467c7c0e2 100644 --- a/Development/nmos/settings.h +++ b/Development/nmos/settings.h @@ -142,6 +142,7 @@ namespace nmos const web::json::field_as_integer_or channelmapping_port{ U("channelmapping_port"), 3215 }; // system_port [node]: used to construct request URLs for the System API (if not discovered via DNS-SD) const web::json::field_as_integer_or system_port{ U("system_port"), 10641 }; + // control_protocol_ws_port [node]: used to construct request URLs for the Control Protocol websocket, or negative to disable the control protocol features const web::json::field_as_integer_or control_protocol_ws_port{ U("control_protocol_ws_port"), 3218 }; // listen_backlog [registry, node]: the maximum length of the queue of pending connections, or zero for the implementation default (the implementation may not honour this value) From 52dc55a420fb09ad5e7c9f6bb61f6c0e843de6b0 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Sun, 20 Aug 2023 00:31:56 +0100 Subject: [PATCH 026/250] Remove unused code --- Development/nmos/control_protocol_ws_api.cpp | 730 +------------------ 1 file changed, 1 insertion(+), 729 deletions(-) diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 8e5f31752..cb1857d7d 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -72,7 +72,7 @@ namespace nmos } return value::null(); - }; + } // hmm, change method_id to struct, and bring in method handlers via the control_classes nmos::experimental::method find_method(const web::json::value& method_id, const nc_class_id& class_id_, const nmos::experimental::control_classes& control_classes) @@ -636,734 +636,6 @@ namespace nmos return NULL; } - - /* - std::pair create_properties_methods(nmos::node_model& model, const nc_class_id& class_id_, const nmos::experimental::control_classes& control_classes) - { - using web::json::value; - using web::json::value_of; - - // hmm, methods should also be passing in via the control_class::methods - - // NcObject methods implementation - // Get property value - const auto get = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto& resources = model.control_protocol_resources; - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != resource) - { - // where arguments is the property id = (level, index) - const auto& property_id = nmos::fields::nc::id(arguments); - - // find the relevant nc_property_descriptor - const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); - if (!property.is_null()) - { - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(property))); - } - - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do Get"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); - } - - // resource not found for the given oid - utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << U(" to do Get"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - }; - // Set property value - const auto set = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto& resources = model.control_protocol_resources; - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != resource) - { - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& val = nmos::fields::nc::value(arguments); - - // find the relevant nc_property_descriptor - const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); - if (!property.is_null()) - { - if (nmos::fields::nc::is_read_only(property)) - { - return details::make_control_protocol_response(handle, { details::nc_method_status::read_only }); - } - - if ((val.is_null() && !nmos::fields::nc::is_nullable(property)) - || (val.is_array() && !nmos::fields::nc::is_sequence(property))) - { - return details::make_control_protocol_response(handle, { details::nc_method_status::parameter_error }); - } - - resources.modify(resource, [&](nmos::resource& resource) - { - resource.data[nmos::fields::nc::name(property)] = val; - - resource.updated = strictly_increasing_update(resources); - }); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); - } - - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << " to do Set"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); - } - - // resource not found for the given oid - utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to do Set"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - }; - // Get sequence item - const auto get_sequence_item = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto& resources = model.control_protocol_resources; - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != resource) - { - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& index = nmos::fields::nc::index(arguments); - - // find the relevant nc_property_descriptor - const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); - if (!property.is_null()) - { - if (!nmos::fields::nc::is_sequence(property)) - { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); - } - - auto& data = resource->data.at(nmos::fields::nc::name(property)); - - if (!data.is_null() && data.as_array().size() > (size_t)index) - { - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, data.at(index)); - } - - // out of bound - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do GetSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); - } - - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do GetSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); - } - - // resource not found for the given oid - utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << U(" to do GetSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - }; - // Set sequence item - const auto set_sequence_item = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto& resources = model.control_protocol_resources; - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != resource) - { - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& index = nmos::fields::nc::index(arguments); - const auto& val = nmos::fields::nc::value(arguments); - - // find the relevant nc_property_descriptor - const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); - if (!property.is_null()) - { - if (!nmos::fields::nc::is_sequence(property)) - { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do SetSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); - } - - auto& data = resource->data.at(nmos::fields::nc::name(property)); - - if (!data.is_null() && data.as_array().size() > (size_t)index) - { - resources.modify(resource, [&](nmos::resource& resource) - { - resource.data[nmos::fields::nc::name(property)][index] = val; - - resource.updated = strictly_increasing_update(resources); - }); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); - } - - // out of bound - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do SetSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); - } - - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do SetSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); - } - - // resource not found for the given oid - utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << U(" to do SetSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - }; - // Add item to sequence - const auto add_sequence_item = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto& resources = model.control_protocol_resources; - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != resource) - { - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& val = nmos::fields::nc::value(arguments); - - // find the relevant nc_property_descriptor - const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); - if (!property.is_null()) - { - if (!nmos::fields::nc::is_sequence(property)) - { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do AddSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); - } - - auto& data = resource->data.at(nmos::fields::nc::name(property)); - - resources.modify(resource, [&](nmos::resource& resource) - { - auto& sequence = resource.data[nmos::fields::nc::name(property)]; - if (data.is_null()) { sequence = value::array(); } - web::json::push_back(sequence, val); - - resource.updated = strictly_increasing_update(resources); - }); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, uint32_t(data.as_array().size() - 1)); - } - - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do AddSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); - } - - // resource not found for the given oid - utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << U(" to do AddSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - }; - // Delete sequence item - const auto remove_sequence_item = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto& resources = model.control_protocol_resources; - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != resource) - { - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& index = nmos::fields::nc::index(arguments); - - // find the relevant nc_property_descriptor - const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); - if (!property.is_null()) - { - if (!nmos::fields::nc::is_sequence(property)) - { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do RemoveSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); - } - - auto& data = resource->data.at(nmos::fields::nc::name(property)); - - if (!data.is_null() && data.as_array().size() > (size_t)index) - { - resources.modify(resource, [&](nmos::resource& resource) - { - auto& sequence = resource.data[nmos::fields::nc::name(property)].as_array(); - sequence.erase(index); - - resource.updated = strictly_increasing_update(resources); - }); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); - } - - // out of bound - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do RemoveSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); - } - - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do RemoveSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); - } - - // resource not found for the given oid - utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << U(" to do RemoveSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - }; - // Get sequence length - const auto get_sequence_length = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto& resources = model.control_protocol_resources; - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != resource) - { - const auto& property_id = nmos::fields::nc::id(arguments); - - // find the relevant nc_property_descriptor - const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); - if (!property.is_null()) - { - if (!nmos::fields::nc::is_sequence(property)) - { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceLength"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); - } - - auto& data = resource->data.at(nmos::fields::nc::name(property)); - - if (nmos::fields::nc::is_nullable(property)) - { - // can be null - if (data.is_null()) - { - // null - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, value::null()); - } - } - else - { - // cannot be null - if (data.is_null()) - { - // null - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << " is a null sequence to do GetSequenceLength"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); - } - } - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, uint32_t(data.as_array().size())); - } - - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << " to do GetSequenceLength"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); - } - - // resource not found for the given oid - utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to do GetSequenceLength"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - }; - - // NcBlock methods implementation - // Gets descriptors of members of the block - const auto get_member_descriptors = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto& resources = model.control_protocol_resources; - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != resource) - { - const auto& recurse = nmos::fields::nc::recurse(arguments); // If recurse is set to true, nested members is to be retrieved - - auto descriptors = value::array(); - nmos::get_member_descriptors(resources, nmos::find_resource(resources, utility::s2us(std::to_string(oid))), recurse, descriptors.as_array()); - - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptors); - } - - // resource not found for the given oid - utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << U(" to do GetMemberDescriptors"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - }; - // Finds member(s) by path - const auto find_members_by_path = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto& resources = model.control_protocol_resources; - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != resource) - { - // Relative path to search for (MUST not include the role of the block targeted by oid) - const auto& path = nmos::fields::nc::path(arguments); - - if (0 == path.size()) - { - // empty path - return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty path to do FindMembersByPath")); - } - - auto nc_block_member_descriptors = value::array(); - - for (const auto& role : path) - { - // look for the role in members - if (resource->data.has_field(nmos::fields::nc::members)) - { - auto& members = nmos::fields::nc::members(resource->data); - auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& nc_block_member_descriptor) - { - return role.as_string() == nmos::fields::nc::role(nc_block_member_descriptor); - }); - - if (members.end() != member_found) - { - web::json::push_back(nc_block_member_descriptors, *member_found); - - // use oid to look for the next resource - resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(*member_found)))); - } - else - { - // no role - utility::stringstream_t ss; - ss << U("role: ") << role.as_string() << U(" not found to do FindMembersByPath"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - } - } - else - { - // no members - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, U("no members to do FindMembersByPath")); - } - } - - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, nc_block_member_descriptors); - } - - // resource not found for the given oid - utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << " to do FindMembersByPath"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - }; - // Finds members with given role name or fragment - const auto find_members_by_role = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) - { - const auto& role = nmos::fields::nc::role(arguments); // Role text to search for - const auto& case_sensitive = nmos::fields::nc::case_sensitive(arguments); // Signals if the comparison should be case sensitive - const auto& match_whole_string = nmos::fields::nc::match_whole_string(arguments); // TRUE to only return exact matches - const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks - - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto& resources = model.control_protocol_resources; - - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != resource) - { - if (role.empty()) - { - // empty role - return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty role to do FindMembersByRole")); - } - - auto descriptors = value::array(); - nmos::find_members_by_role(resources, nmos::find_resource(resources, utility::s2us(std::to_string(oid))), role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); - - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptors); - } - - // resource not found for the given oid - utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << U(" to do FindMembersByRole"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - }; - // Finds members with given class id - const auto find_members_by_class_id = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) - { - const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for - const auto& include_derived = nmos::fields::nc::include_derived(arguments); // If TRUE it will also include derived class descriptors - const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks - - if (class_id.empty()) - { - // empty class_id - return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty classId to do FindMembersByClassId")); - } - - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto& resources = model.control_protocol_resources; - - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != resource) - { - auto descriptors = value::array(); - nmos::find_members_by_class_id(resources, nmos::find_resource(resources, utility::s2us(std::to_string(oid))), class_id, include_derived, recurse, descriptors.as_array()); - - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptors); - } - - // resource not found for the given oid - utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << U(" to do FindMembersByClassId"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - }; - - // NcClassManager methods implementation - // Get a single class descriptor - const auto get_control_class = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) - { - const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for - const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements - - if (class_id.empty()) - { - // empty class_id - return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty classId to do GetControlClass")); - } - - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto& resources = model.control_protocol_resources; - - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != resource) - { - auto class_found = control_classes.find(make_nc_class_id(class_id)); - - if (control_classes.end() != class_found) - { - auto id = class_id; - - auto description = class_found->second.description; - auto name = class_found->second.name; - auto fixed_role = class_found->second.fixed_role; - auto properties = class_found->second.properties; - auto methods = class_found->second.methods; - auto events = class_found->second.events; - - id.pop_back(); - - if (include_inherited) - { - while (!id.empty()) - { - auto found = control_classes.find(make_nc_class_id(id)); - if (control_classes.end() != found) - { - for (const auto& property : found->second.properties.as_array()) { web::json::push_back(properties, property); } - for (const auto& method : found->second.methods.as_array()) { web::json::push_back(methods, method); } - for (const auto& event : found->second.events.as_array()) { web::json::push_back(events, event); } - } - id.pop_back(); - } - } - auto descriptor = details::make_nc_class_descriptor(description, class_id, name, fixed_role, properties, methods, events); - - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptor); - } - - return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("classId not found")); - } - - // resource not found for the given oid - utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << U(" to do GetControlClass"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - }; - // Get a single datatype descriptor - const auto get_datatype = [&model](int32_t handle, int32_t oid, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) - { - const auto& name = nmos::fields::nc::name(arguments); // name of datatype - const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements - - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto& resources = model.control_protocol_resources; - - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != resource) - { - if (name.empty()) - { - // empty name - return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty name to do GetDatatype")); - } - - auto datatype_found = datatypes.find(name); - - if (datatypes.end() != datatype_found) - { - auto descriptor = datatype_found->second.descriptor; - - if (include_inherited) - { - const auto& type = nmos::fields::nc::type(descriptor); - if(details::nc_datatype_type::Struct == type) - { - auto descriptor_ = descriptor; - - for (;;) - { - const auto& parent_type = descriptor_.at(nmos::fields::nc::parent_type); - if (!parent_type.is_null()) - { - auto datatype_found_ = datatypes.find(parent_type.as_string()); - if (datatypes.end() != datatype_found_) - { - descriptor_ = datatype_found_->second.descriptor; - const auto& fields = nmos::fields::nc::fields(descriptor_); - for (const auto& field : fields) - { - web::json::push_back(descriptor.at(nmos::fields::nc::fields), field); - } - } - } - else - { - break; - } - } - } - } - - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptor); - } - - return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("name not found")); - } - - // resource not found for the given oid - utility::stringstream_t ss; - ss << U("unknown oid: ") << oid << U(" to do GetDatatype"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); - }; - - // method handlers for the different classes - details::methods nc_object_method_handlers; // method_id vs NcObject method_handler - details::methods nc_block_method_handlers; // method_id vs NcBlock method_handler - details::methods nc_worker_method_handlers; // method_id vs NcWorker method_handler - details::methods nc_manager_method_handlers; // method_id vs NcManager method_handler - details::methods nc_device_manager_method_handlers; // method_id vs NcDeviceManager method_handler - details::methods nc_class_manager_method_handlers; // method_id vs NcClassManager method_handler - - // NcObject methods - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject - nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 1 } })] = get; - nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 2 } })] = set; - nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 3 } })] = get_sequence_item; - nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 4 } })] = set_sequence_item; - nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 5 } })] = add_sequence_item; - nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 6 } })] = remove_sequence_item; - nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 7 } })] = get_sequence_length; - - // NcBlock methods - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock - nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 1 } })] = get_member_descriptors; - nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 2 } })] = find_members_by_path; - nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 3 } })] = find_members_by_role; - nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 4 } })] = find_members_by_class_id; - - // NcWorker has no extended method - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker - - // NcManager has no extended method - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager - - // NcDeviceManger has no extended method - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager - - // NcClassManager methods - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - nc_class_manager_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 1 } })] = get_control_class; - nc_class_manager_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 2 } })] = get_datatype; - - value properties = value::array(); // combined base classes nc_property_descriptor(s) for the required class_id - details::methods methods; // list of combined base classes method handlers - - auto class_found = control_classes.find(make_nc_class_id(class_id_)); - if (control_classes.end() != class_found) - { - // hmm, update the array of properties, will be updated the list of method handlers - auto insert_properties = [&properties, &control_classes](const nc_class_id& class_id_) - { - auto class_id = make_nc_class_id(class_id_); - auto class_id_found = control_classes.find(class_id); - if (control_classes.end() != class_id_found) - { - auto& nc_class_properties = class_id_found->second.properties.as_array(); - for (auto& nc_class_property : nc_class_properties) - { - web::json::push_back(properties, nc_class_property); - } - } - }; - - auto class_id = class_id_; - while (class_id.size()) - { - insert_properties(class_id); - - // hmm, to be deleted, once the methods are passed in - if (details::nc_object_class_id == class_id) - { - methods.insert(nc_object_method_handlers.begin(), nc_object_method_handlers.end()); - } - else if (details::nc_block_class_id == class_id) - { - methods.insert(nc_block_method_handlers.begin(), nc_block_method_handlers.end()); - } - else if (details::nc_manager_class_id == class_id) - { - methods.insert(nc_manager_method_handlers.begin(), nc_manager_method_handlers.end()); - } - else if (details::nc_device_manager_class_id == class_id) - { - methods.insert(nc_device_manager_method_handlers.begin(), nc_device_manager_method_handlers.end()); - } - else if (details::nc_class_manager_class_id == class_id) - { - methods.insert(nc_class_manager_method_handlers.begin(), nc_class_manager_method_handlers.end()); - } - class_id.pop_back(); - } - } - else - { - throw std::runtime_error("unknown control class"); - } - - return { properties, methods }; - } - */ } // IS-12 Control Protocol WebSocket API From 3924c6b967342e0b3f8ebe0e122d8bfef117ad79 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 22 Aug 2023 18:16:38 +0100 Subject: [PATCH 027/250] Fix find_members_by_path and write log on method --- Development/nmos/control_protocol_ws_api.cpp | 77 ++++++++++++++------ 1 file changed, 53 insertions(+), 24 deletions(-) diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index cb1857d7d..595c918b9 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -82,13 +82,14 @@ namespace nmos // NcObject methods implementation // Get property value - const auto get = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto get = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - // where arguments is the property id = (level, index) const auto& property_id = nmos::fields::nc::id(arguments); + slog::log(gate, SLOG_FLF) << "Get property: " << property_id.to_string(); + // find the relevant nc_property_descriptor const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); if (!property.is_null()) @@ -102,13 +103,15 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); }; // Set property value - const auto set = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto set = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... const auto& property_id = nmos::fields::nc::id(arguments); const auto& val = nmos::fields::nc::value(arguments); + slog::log(gate, SLOG_FLF) << "Set property: " << property_id.to_string() << " value: " << val.to_string(); + // find the relevant nc_property_descriptor const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); if (!property.is_null()) @@ -139,13 +142,15 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); }; // Get sequence item - const auto get_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto get_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... const auto& property_id = nmos::fields::nc::id(arguments); const auto& index = nmos::fields::nc::index(arguments); + slog::log(gate, SLOG_FLF) << "Get sequence item: " << property_id.to_string() << " index: " << index; + // find the relevant nc_property_descriptor const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); if (!property.is_null()) @@ -177,7 +182,7 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); }; // Set sequence item - const auto set_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto set_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -185,6 +190,8 @@ namespace nmos const auto& index = nmos::fields::nc::index(arguments); const auto& val = nmos::fields::nc::value(arguments); + slog::log(gate, SLOG_FLF) << "Set sequence item: " << property_id.to_string() << " index: " << index << " value: " << val.to_string(); + // find the relevant nc_property_descriptor const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); if (!property.is_null()) @@ -222,13 +229,15 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); }; // Add item to sequence - const auto add_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto add_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... const auto& property_id = nmos::fields::nc::id(arguments); const auto& val = nmos::fields::nc::value(arguments); + slog::log(gate, SLOG_FLF) << "Add sequence item: " << property_id.to_string() << " value: " << val.to_string(); + // find the relevant nc_property_descriptor const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); if (!property.is_null()) @@ -260,13 +269,15 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); }; // Delete sequence item - const auto remove_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto remove_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... const auto& property_id = nmos::fields::nc::id(arguments); const auto& index = nmos::fields::nc::index(arguments); + slog::log(gate, SLOG_FLF) << "Remove sequence item: " << property_id.to_string() << " index: " << index; + // find the relevant nc_property_descriptor const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); if (!property.is_null()) @@ -305,12 +316,14 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); }; // Get sequence length - const auto get_sequence_length = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto get_sequence_length = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... const auto& property_id = nmos::fields::nc::id(arguments); + slog::log(gate, SLOG_FLF) << "Get sequence length: " << property_id.to_string(); + // find the relevant nc_property_descriptor const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); if (!property.is_null()) @@ -355,25 +368,29 @@ namespace nmos }; // NcBlock methods implementation - // Gets descriptors of members of the block - const auto get_member_descriptors = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + // Get descriptors of members of the block + const auto get_member_descriptors = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes&, const nmos::experimental::datatypes&, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... const auto& recurse = nmos::fields::nc::recurse(arguments); // If recurse is set to true, nested members is to be retrieved + slog::log(gate, SLOG_FLF) << "Get descriptors of members of the block: " << "recurse: " << recurse; + auto descriptors = value::array(); nmos::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptors); }; // Finds member(s) by path - const auto find_members_by_path = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto find_members_by_path = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes&, const nmos::experimental::datatypes&, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... // Relative path to search for (MUST not include the role of the block targeted by oid) - const auto& path = nmos::fields::nc::path(arguments); + const auto& path = arguments.at(nmos::fields::nc::path); + + slog::log(gate, SLOG_FLF) << "Find member(s) by path: " << "path: " << path.to_string(); if (0 == path.size()) { @@ -382,21 +399,22 @@ namespace nmos } auto nc_block_member_descriptors = value::array(); + value nc_block_member_descriptor; - for (const auto& role : path) + for (const auto& role : path.as_array()) { // look for the role in members if (resource->data.has_field(nmos::fields::nc::members)) { auto& members = nmos::fields::nc::members(resource->data); auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& nc_block_member_descriptor) - { - return role.as_string() == nmos::fields::nc::role(nc_block_member_descriptor); - }); + { + return role.as_string() == nmos::fields::nc::role(nc_block_member_descriptor); + }); if (members.end() != member_found) { - web::json::push_back(nc_block_member_descriptors, *member_found); + nc_block_member_descriptor = *member_found; // use oid to look for the next resource resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(*member_found)))); @@ -416,17 +434,20 @@ namespace nmos } } + web::json::push_back(nc_block_member_descriptors, nc_block_member_descriptor); return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, nc_block_member_descriptors); }; // Finds members with given role name or fragment - const auto find_members_by_role = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto find_members_by_role = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes&, const nmos::experimental::datatypes&, slog::base_gate& gate) { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + const auto& role = nmos::fields::nc::role(arguments); // Role text to search for const auto& case_sensitive = nmos::fields::nc::case_sensitive(arguments); // Signals if the comparison should be case sensitive const auto& match_whole_string = nmos::fields::nc::match_whole_string(arguments); // TRUE to only return exact matches const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + slog::log(gate, SLOG_FLF) << "Find members with given role name or fragment: " << "role: " << role; if (role.empty()) { @@ -440,12 +461,16 @@ namespace nmos return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptors); }; // Finds members with given class id - const auto find_members_by_class_id = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto find_members_by_class_id = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes&, const nmos::experimental::datatypes&, slog::base_gate& gate) { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for const auto& include_derived = nmos::fields::nc::include_derived(arguments); // If TRUE it will also include derived class descriptors const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks + slog::log(gate, SLOG_FLF) << "Find members with given class id: " << "class_id: " << details::make_nc_class_id(class_id).to_string(); + if (class_id.empty()) { // empty class_id @@ -462,11 +487,13 @@ namespace nmos // NcClassManager methods implementation // Get a single class descriptor - const auto get_control_class = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto get_control_class = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) { const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements + slog::log(gate, SLOG_FLF) << "Get a single class descriptor: " << "class_id: " << details::make_nc_class_id(class_id).to_string(); + if (class_id.empty()) { // empty class_id @@ -512,12 +539,14 @@ namespace nmos return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("classId not found")); }; // Get a single datatype descriptor - const auto get_datatype = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes& datatypes) + const auto get_datatype = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes&, const nmos::experimental::datatypes& datatypes, slog::base_gate& gate) { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + const auto& name = nmos::fields::nc::name(arguments); // name of datatype const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + slog::log(gate, SLOG_FLF) << "Get a single datatype descriptor: " << "name: " << name; if (name.empty()) { @@ -844,7 +873,7 @@ namespace nmos if (method) { // execute the relevant method handler, then accumulating up their response to reponses - web::json::push_back(responses, method(resources, resource, handle, arguments, get_control_protocol_classes(), get_control_protocol_datatypes())); + web::json::push_back(responses, method(resources, resource, handle, arguments, get_control_protocol_classes(), get_control_protocol_datatypes(), gate)); } else { From 91457529523082fc20d3887aa31de274f9ddfeba Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 22 Aug 2023 18:17:24 +0100 Subject: [PATCH 028/250] Remove un-used code --- .../nmos/control_protocol_handlers.cpp | 29 ++----------------- Development/nmos/control_protocol_handlers.h | 7 ----- 2 files changed, 3 insertions(+), 33 deletions(-) diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index 609053ea4..5e52f0700 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -8,7 +8,7 @@ namespace nmos { return [&]() { - slog::log(gate, SLOG_FLF) << "Retrieve all control classes from cache"; + slog::log(gate, SLOG_FLF) << "Retrieve all control classes from cache"; auto lock = control_protocol_state.read_lock(); @@ -16,34 +16,11 @@ namespace nmos }; } - //get_control_protocol_class_handler make_get_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) - //{ - // return [&](const details::nc_class_id& class_id) - // { - // using web::json::value; - - // slog::log(gate, SLOG_FLF) << "Retrieve control class from cache"; - - // auto lock = control_protocol_state.read_lock(); - - // auto class_id_data = details::make_nc_class_id(class_id); - - // auto& control_classes = control_protocol_state.control_classes; - // auto found = control_classes.find(class_id_data); - // if (control_classes.end() != found) - // { - // return found->second; - // } - - // return experimental::control_class{ value::array(), value::array(), value::array() }; - // }; - //} - add_control_protocol_class_handler make_add_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) { return [&](const details::nc_class_id& class_id, const experimental::control_class& control_class) { - slog::log(gate, SLOG_FLF) << "Add control class to cache"; + slog::log(gate, SLOG_FLF) << "Add control class to cache"; auto lock = control_protocol_state.write_lock(); @@ -64,7 +41,7 @@ namespace nmos { return [&]() { - slog::log(gate, SLOG_FLF) << "Retrieve all datatypes from cache"; + slog::log(gate, SLOG_FLF) << "Retrieve all datatypes from cache"; auto lock = control_protocol_state.read_lock(); diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index d0af0c30e..565bf6c26 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -21,10 +21,6 @@ namespace nmos // this callback should not throw exceptions typedef std::function get_control_protocol_classes_handler; - // callback to retrieve a specific control protocol class - // this callback should not throw exceptions -// typedef std::function get_control_protocol_class_handler; - // callback to add user control protocol class // this callback should not throw exceptions typedef std::function add_control_protocol_class_handler; @@ -36,9 +32,6 @@ namespace nmos // construct callback to retrieve all control protocol classes get_control_protocol_classes_handler make_get_control_protocol_classes_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); - // construct callback to retrieve control protocol class -// get_control_protocol_class_handler make_get_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); - // construct callback to add control protocol class add_control_protocol_class_handler make_add_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); From 0ef34d0a57c75a8b1105d7a622fbf9cea277e374 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 22 Aug 2023 18:18:40 +0100 Subject: [PATCH 029/250] Add Log gate to method --- Development/nmos/control_protocol_state.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index c15e2fd65..82f4e755f 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -7,6 +7,8 @@ #include "nmos/mutex.h" #include "nmos/resources.h" +namespace slog { class base_gate; } + namespace nmos { namespace experimental @@ -34,7 +36,7 @@ namespace nmos typedef std::map datatypes; // methods defnitions - typedef std::function method; + typedef std::function method; typedef std::map methods; // method_id vs method handler struct control_protocol_state From dba104202c3f1c2a7378e1d79d91adc9aa0fd4ed Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 22 Aug 2023 18:20:38 +0100 Subject: [PATCH 030/250] Move nc_class_id definition from control_protocol_resource to control_protocol_utils --- Development/nmos/control_protocol_utils.cpp | 8 ++++++++ Development/nmos/control_protocol_utils.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 106a55a7d..a19f58699 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -42,6 +42,14 @@ namespace nmos { return is_control_class(nc_class_manager_class_id, class_id); } + + nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const nc_class_id& suffix) + { + nc_class_id class_id = prefix; + class_id.push_back(authority_key); + class_id.insert(class_id.end(), suffix.begin(), suffix.end()); + return class_id; + } } void get_member_descriptors(const resources& resources, resources::iterator resource, bool recurse, web::json::array& descriptors) diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index b90f63574..ec59aa747 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -16,6 +16,8 @@ namespace nmos bool is_nc_device_manager(const nc_class_id& class_id); bool is_nc_class_manager(const nc_class_id& class_id); + + nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const nc_class_id& suffix); } void get_member_descriptors(const resources& resources, resources::iterator resource, bool recurse, web::json::array& descriptors); From 38077d253a9b4dda0342fc69567f3b3bcdfaaeeb Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 22 Aug 2023 18:21:37 +0100 Subject: [PATCH 031/250] Add nested block examples --- .../nmos-cpp-node/node_implementation.cpp | 72 +++++++++++++++++-- .../nmos-cpp-node/node_implementation.h | 2 +- .../nmos/control_protocol_resource.cpp | 13 +++- Development/nmos/control_protocol_resource.h | 43 +++++------ .../nmos/control_protocol_resources.cpp | 36 ++++++++-- Development/nmos/control_protocol_resources.h | 10 +++ 6 files changed, 142 insertions(+), 34 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 2d4774f13..6d771ae12 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -23,6 +23,7 @@ #include "nmos/connection_events_activation.h" #include "nmos/control_protocol_resources.h" #include "nmos/control_protocol_state.h" +#include "nmos/control_protocol_utils.h" #include "nmos/events_resources.h" #include "nmos/format.h" #include "nmos/group_hint.h" @@ -46,6 +47,10 @@ #include "nmos/video_jxsv.h" #include "sdp/sdp.h" +// hmm, for IS-12 gain control +#include "nmos/resource.h" +#include "nmos/is12_versions.h" + // example node implementation details namespace impl { @@ -188,7 +193,7 @@ namespace impl } // forward declarations for node_implementation_thread -void node_implementation_init(nmos::node_model& model, const nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); +void node_implementation_init(nmos::node_model& model, nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); void node_implementation_run(nmos::node_model& model, slog::base_gate& gate); nmos::connection_resource_auto_resolver make_node_implementation_auto_resolver(const nmos::settings& settings); nmos::connection_sender_transportfile_setter make_node_implementation_transportfile_setter(const nmos::resources& node_resources, const nmos::settings& settings); @@ -198,7 +203,7 @@ struct node_implementation_init_exception {}; // This is an example of how to integrate the nmos-cpp library with a device-specific underlying implementation. // It constructs and inserts a node resource and some sub-resources into the model, based on the model settings, // starts background tasks to emit regular events from the temperature event source, and then waits for shutdown. -void node_implementation_thread(nmos::node_model& model, const nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate_) +void node_implementation_thread(nmos::node_model& model, nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate_) { nmos::details::omanip_gate gate{ gate_, nmos::stash_category(impl::categories::node_implementation) }; @@ -234,7 +239,7 @@ void node_implementation_thread(nmos::node_model& model, const nmos::experimenta } } -void node_implementation_init(nmos::node_model& model, const nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) +void node_implementation_init(nmos::node_model& model, nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) { using web::json::value; using web::json::value_from_elements; @@ -897,17 +902,70 @@ void node_implementation_init(nmos::node_model& model, const nmos::experimental: if (!insert_resource_after(delay_millis, model.channelmapping_resources, std::move(channelmapping_output), gate)) throw node_implementation_init_exception(); } + // example of using control protocol if (0 <= nmos::fields::control_protocol_ws_port(model.settings)) { + // example to create a custom Gain control class + const auto gain_control_class_id = nmos::details::make_nc_class_id(nmos::details::nc_worker_class_id, 0, { 1 }); + const web::json::field_as_number gain_value{ U("gainValue") }; + auto make_gain_control_properties = [&gain_value]() + { + auto properties = value::array(); + web::json::push_back(properties, nmos::details::make_nc_property_descriptor(value::string(U("Gain value")), nmos::details::make_nc_property_id(3, 1), gain_value, value::string(U("NcFloat32")), false, false, false, false)); + return properties; + }; + nmos::experimental::control_class gain_control_class = { value::string(U("Gain control class descriptor")), gain_control_class_id, U("GainControl"), value::null(), make_gain_control_properties(), value::array(), value::array()}; + control_protocol_state.control_classes[nmos::details::make_nc_class_id(gain_control_class_id)] = gain_control_class; + // helper function to create Gain control instance + auto make_gain_control = [&gain_value, &gain_control_class_id](nmos::details::nc_oid oid, nmos::details::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, float gain = 0, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null()) + { + auto data = nmos::details::make_nc_worker(gain_control_class_id, oid, true, owner, role, value::string(user_label), touchpoints, runtime_property_constraints, true); + data[gain_value] = value::number(gain); + return nmos::resource{ nmos::is12_versions::v1_0, nmos::types::nc_worker, std::move(data), true }; + }; + // example root block auto root_block = nmos::make_root_block(); + + nmos::details::nc_oid oid{ 2 }; // example device manager - auto device_manager = nmos::make_device_manager(2, root_block, model.settings); - if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(device_manager), gate)) throw node_implementation_init_exception(); + auto device_manager = nmos::make_device_manager(oid++, root_block, model.settings); + // example class manager - auto class_manager = nmos::make_class_manager(3, root_block, control_protocol_state); + auto class_manager = nmos::make_class_manager(oid++, root_block, control_protocol_state); + + // example stereo gain + const auto& root_block_oid = nmos::fields::nc::oid(root_block.data); + const auto stereo_gain_oid = oid++; + // add master-gain and channel-gain + auto stereo_gain = nmos::make_block(stereo_gain_oid, root_block_oid, U("stereo-gain"), U("Stereo gain")); + + // example channel gain + const auto channel_gain_oid = oid++; + // example left/right gains + auto left_gain = make_gain_control(oid++, channel_gain_oid, U("left-gain"), U("Left gain")); + auto right_gain = make_gain_control(oid++, channel_gain_oid, U("right-gain"), U("Right gain")); + // add left-gain and right-gain to channel gain + auto channel_gain = nmos::make_block(channel_gain_oid, stereo_gain_oid, U("channel-gain"), U("Channel gain")); + nmos::add_member_to_block(U("Left channel gain"), left_gain.data, channel_gain.data); + nmos::add_member_to_block(U("Right channel gain"), right_gain.data, channel_gain.data); + + // example master-gain + auto master_gain = make_gain_control(oid++, channel_gain_oid, U("master-gain"), U("Master gain")); + // add master-gain and channel-gain to stereo-gain + nmos::add_member_to_block(U("Master gain block"), master_gain.data, stereo_gain.data); + nmos::add_member_to_block(U("Channel gain block"), channel_gain.data, stereo_gain.data); + // add stereo-gain to root-block + nmos::add_member_to_block(U("Stereo gain block"), stereo_gain.data, root_block.data); + + // insert resources to model + if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(left_gain), gate)) throw node_implementation_init_exception(); + if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(right_gain), gate)) throw node_implementation_init_exception(); + if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(master_gain), gate)) throw node_implementation_init_exception(); + if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(channel_gain), gate)) throw node_implementation_init_exception(); + if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(stereo_gain), gate)) throw node_implementation_init_exception(); + if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(device_manager), gate)) throw node_implementation_init_exception(); if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(class_manager), gate)) throw node_implementation_init_exception(); - // insert root block to model if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(root_block), gate)) throw node_implementation_init_exception(); } } diff --git a/Development/nmos-cpp-node/node_implementation.h b/Development/nmos-cpp-node/node_implementation.h index c5d6504da..3c6b295c3 100644 --- a/Development/nmos-cpp-node/node_implementation.h +++ b/Development/nmos-cpp-node/node_implementation.h @@ -20,7 +20,7 @@ namespace nmos // This is an example of how to integrate the nmos-cpp library with a device-specific underlying implementation. // It constructs and inserts a node resource and some sub-resources into the model, based on the model settings, // starts background tasks to emit regular events from the temperature event source, and then waits for shutdown. -void node_implementation_thread(nmos::node_model& model, const nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); +void node_implementation_thread(nmos::node_model& model, nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); // This constructs all the callbacks used to integrate the example device-specific underlying implementation // into the server instance for the NMOS Node. diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index b456443f7..731dc00d8 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -1437,6 +1437,17 @@ namespace nmos return data; } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker + web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled) + { + using web::json::value; + + auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); + data[nmos::fields::nc::enabled] = value::boolean(enabled); + + return data; + } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { @@ -1451,7 +1462,7 @@ namespace nmos using web::json::value; auto data = details::make_nc_manager(nc_device_manager_class_id, oid, true, owner, U("DeviceManager"), user_label, touchpoints, runtime_property_constraints); - data[nmos::fields::nc::nc_version] = value::string(U("v1.0")); + data[nmos::fields::nc::nc_version] = value::string(U("v1.0.0")); data[nmos::fields::nc::manufacturer] = manufacturer; data[nmos::fields::nc::product] = product; data[nmos::fields::nc::serial_number] = value::string(serial_number); diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 8b1b4a892..387bb6978 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -95,12 +95,12 @@ namespace nmos { enum state { - Unknown = 0, // Unknown - NormalOperation = 1, // Normal operation - Initializing = 2, // Device is initializing - Updating = 3, // Device is performing a software or firmware update - LicensingError = 4, // Device is experiencing a licensing error - InternalError = 5 // Device is experiencing an internal error + unknown = 0, // Unknown + normal_operation = 1, // Normal operation + initializing = 2, // Device is initializing + updating = 3, // Device is performing a software or firmware update + licensing_error = 4, // Device is experiencing a licensing error + internal_error = 5 // Device is experiencing an internal error }; } @@ -109,12 +109,12 @@ namespace nmos { enum cause { - Unknown = 0, // Unknown - Power_on = 1, // Power on - InternalError = 2, // Internal error - Upgrade = 3, // Upgrade - Controller_request = 4, // Controller request - ManualReset = 5 // Manual request from the front panel + unknown = 0, // Unknown + power_on = 1, // Power on + internal_error = 2, // Internal error + upgrade = 3, // Upgrade + controller_request = 4, // Controller request + manual_reset = 5 // Manual request from the front panel }; } @@ -124,10 +124,10 @@ namespace nmos { enum status { - Undefined = 0, // This is the value when there is no receiver - Connected = 1, // Connected to a stream - Disconnected = 2, // Not connected to a stream - ConnectionError = 3 // A connection error was encountered + undefined = 0, // This is the value when there is no receiver + connected = 1, // Connected to a stream + disconnected = 2, // Not connected to a stream + connection_error = 3 // A connection error was encountered }; } @@ -137,10 +137,10 @@ namespace nmos { enum status { - Undefined = 0, // This is the value when there's no connection. - PayloadOK = 1, // Payload is being received without errors and is the correct type - PayloadFormatUnsupported = 2, // Payload is being received but is of an unsupported type - PayloadError = 3 // A payload error was encountered + undefined = 0, // This is the value when there's no connection. + payload_ok = 1, // Payload is being received without errors and is the correct type + payload_format_unsupported = 2, // Payload is being received but is of an unsupported type + payloadError = 3 // A payload error was encountered }; } @@ -478,6 +478,9 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker + web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index c0aa6a5f8..aa126ce1f 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -1,6 +1,7 @@ #include "nmos/control_protocol_resources.h" #include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_utils.h" #include "nmos/resource.h" #include "nmos/is12_versions.h" @@ -20,10 +21,10 @@ namespace nmos const auto& serial_number = nmos::experimental::fields::serial_number(settings); const auto device_name = value::null(); const auto device_role = value::null(); - const auto& operational_state = details::make_nc_device_operational_state(details::nc_device_generic_state::NormalOperation, value::null()); + const auto& operational_state = details::make_nc_device_operational_state(details::nc_device_generic_state::normal_operation, value::null()); auto data = details::make_nc_device_manager(oid, owner, user_label, value::null(), value::null(), - manufacturer, product, serial_number, value::null(), device_name, device_role, operational_state, details::nc_reset_cause::Unknown); + manufacturer, product, serial_number, value::null(), device_name, device_role, operational_state, details::nc_reset_cause::unknown); // add NcDeviceManager block_member_descriptor to root block members web::json::push_back(root_block_data[nmos::fields::nc::members], @@ -51,13 +52,38 @@ namespace nmos return{ is12_versions::v1_0, types::nc_class_manager, std::move(data), true }; } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock - nmos::resource make_root_block() + // create block resource + nmos::resource make_block(details::nc_oid oid, const web::json::value& owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) { using web::json::value; - auto data = details::make_nc_block(details::nc_block_class_id, 1, true, value::null(), U("root"), value::string(U("Root")), value::null(), value::null(), true, value::array()); + auto data = details::make_nc_block(details::nc_block_class_id, oid, true, owner, role, value::string(user_label), touchpoints, runtime_property_constraints, true, members); return{ is12_versions::v1_0, types::nc_block, std::move(data), true }; } + nmos::resource make_block(details::nc_oid oid, details::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) + { + using web::json::value; + + return make_block(oid, value(owner), role, user_label, touchpoints, runtime_property_constraints, members); + } + + // create Root block resource + nmos::resource make_root_block() + { + using web::json::value; + + return make_block(1, value::null(), U("root"), U("Root")); + } + + // add member to nc_block + bool add_member_to_block(const utility::string_t& description, const web::json::value& nc_block, web::json::value& parent) + { + using web::json::value; + + web::json::push_back(parent[nmos::fields::nc::members], + details::make_nc_block_member_descriptor(value::string(description), nmos::fields::nc::role(nc_block), nmos::fields::nc::oid(nc_block), nmos::fields::nc::constant_oid(nc_block), nc_block.at(nmos::fields::nc::class_id), nc_block.at(nmos::fields::nc::user_label), nmos::fields::nc::oid(parent))); + + return true; + } } diff --git a/Development/nmos/control_protocol_resources.h b/Development/nmos/control_protocol_resources.h index b977043cf..205e16606 100644 --- a/Development/nmos/control_protocol_resources.h +++ b/Development/nmos/control_protocol_resources.h @@ -14,10 +14,20 @@ namespace nmos struct resource; + // create device manager resource nmos::resource make_device_manager(details::nc_oid oid, nmos::resource& root_block, const nmos::settings& settings); + // create class manager resource nmos::resource make_class_manager(details::nc_oid oid, nmos::resource& root_block, const nmos::experimental::control_protocol_state& control_protocol_state); + // create block resource + nmos::resource make_block(details::nc_oid oid, const web::json::value& owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), const web::json::value& members = web::json::value::array()); + nmos::resource make_block(details::nc_oid oid, details::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), const web::json::value& members = web::json::value::array()); + + // help function to add member to block + bool add_member_to_block(const utility::string_t& description, const web::json::value& nc_block, web::json::value& parent); + + // create Root block resource nmos::resource make_root_block(); } From 90a768b9c7a5ef07525a6cf637e7851002734092 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 23 Aug 2023 15:34:14 +0100 Subject: [PATCH 032/250] Extract IS-12 version from the rx ws path --- Development/nmos/control_protocol_ws_api.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 595c918b9..1dabbef21 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -820,9 +820,8 @@ namespace nmos const auto& ws_ncp_path = connection_uri.path(); slog::log(gate, SLOG_FLF) << "Received websocket message: " << msg << " on connection: " << ws_ncp_path; - // hmm todo: extract the version from the ws_ncp_path - const nmos::api_version version = is12_versions::v1_0; - //const nmos::api_version version = nmos::parse_api_version(ws_ncp_path(nmos::patterns::version.name)); + // extract the control protocol api version from the ws_ncp_path + const auto version = nmos::parse_api_version(web::uri::split_path(ws_ncp_path).back()); auto websocket = websockets.right.find(connection_id); if (websockets.right.end() != websocket) From d47b47a41e25b5da14b8a18f7f820391dfb28c58 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 25 Aug 2023 00:48:41 +0100 Subject: [PATCH 033/250] Add helper functions to create non-standard control class, and general tidy up --- Development/cmake/NmosCppLibraries.cmake | 3 +- .../nmos-cpp-node/node_implementation.cpp | 56 +- .../nmos/control_protocol_class_id.cpp | 27 - Development/nmos/control_protocol_class_id.h | 19 - .../nmos/control_protocol_handlers.cpp | 3 +- Development/nmos/control_protocol_handlers.h | 2 +- .../nmos/control_protocol_resource.cpp | 2075 +++++++++-------- Development/nmos/control_protocol_resource.h | 589 ++--- .../nmos/control_protocol_resources.cpp | 86 +- Development/nmos/control_protocol_resources.h | 24 +- Development/nmos/control_protocol_state.cpp | 266 ++- Development/nmos/control_protocol_state.h | 37 +- Development/nmos/control_protocol_typedefs.h | 183 ++ Development/nmos/control_protocol_utils.cpp | 59 +- Development/nmos/control_protocol_utils.h | 17 +- Development/nmos/control_protocol_ws_api.cpp | 155 +- 16 files changed, 1929 insertions(+), 1672 deletions(-) delete mode 100644 Development/nmos/control_protocol_class_id.cpp delete mode 100644 Development/nmos/control_protocol_class_id.h create mode 100644 Development/nmos/control_protocol_typedefs.h diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index a135f74d5..f29a683c7 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -831,7 +831,6 @@ set(NMOS_CPP_NMOS_SOURCES nmos/connection_api.cpp nmos/connection_events_activation.cpp nmos/connection_resources.cpp - nmos/control_protocol_class_id.cpp nmos/control_protocol_handlers.cpp nmos/control_protocol_resource.cpp nmos/control_protocol_resources.cpp @@ -911,11 +910,11 @@ set(NMOS_CPP_NMOS_HEADERS nmos/connection_api.h nmos/connection_events_activation.h nmos/connection_resources.h - nmos/control_protocol_class_id.h nmos/control_protocol_handlers.h nmos/control_protocol_resource.h nmos/control_protocol_resources.h nmos/control_protocol_state.h + nmos/control_protocol_typedefs.h nmos/control_protocol_utils.h nmos/control_protocol_ws_api.h nmos/device_type.h diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 6d771ae12..d7bbbcc36 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -22,6 +22,7 @@ #include "nmos/connection_resources.h" #include "nmos/connection_events_activation.h" #include "nmos/control_protocol_resources.h" +#include "nmos/control_protocol_resource.h" #include "nmos/control_protocol_state.h" #include "nmos/control_protocol_utils.h" #include "nmos/events_resources.h" @@ -905,19 +906,14 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // example of using control protocol if (0 <= nmos::fields::control_protocol_ws_port(model.settings)) { - // example to create a custom Gain control class - const auto gain_control_class_id = nmos::details::make_nc_class_id(nmos::details::nc_worker_class_id, 0, { 1 }); + // example to create a non-standard Gain control class + const auto gain_control_class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 1 }); const web::json::field_as_number gain_value{ U("gainValue") }; - auto make_gain_control_properties = [&gain_value]() - { - auto properties = value::array(); - web::json::push_back(properties, nmos::details::make_nc_property_descriptor(value::string(U("Gain value")), nmos::details::make_nc_property_id(3, 1), gain_value, value::string(U("NcFloat32")), false, false, false, false)); - return properties; - }; - nmos::experimental::control_class gain_control_class = { value::string(U("Gain control class descriptor")), gain_control_class_id, U("GainControl"), value::null(), make_gain_control_properties(), value::array(), value::array()}; - control_protocol_state.control_classes[nmos::details::make_nc_class_id(gain_control_class_id)] = gain_control_class; + std::vector gain_control_properties = { nmos::experimental::make_control_class_property(U("Gain value"), { 3, 1 }, gain_value, U("NcFloat32")) }; + auto gain_control_class = nmos::experimental::make_control_class(U("Gain control class descriptor"), gain_control_class_id, U("GainControl"), gain_control_properties, {}, {}); + control_protocol_state.insert(gain_control_class); // helper function to create Gain control instance - auto make_gain_control = [&gain_value, &gain_control_class_id](nmos::details::nc_oid oid, nmos::details::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, float gain = 0, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null()) + auto make_gain_control = [&gain_value, &gain_control_class_id](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, float gain = 0.0, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null()) { auto data = nmos::details::make_nc_worker(gain_control_class_id, oid, true, owner, role, value::string(user_label), touchpoints, runtime_property_constraints, true); data[gain_value] = value::number(gain); @@ -927,36 +923,40 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // example root block auto root_block = nmos::make_root_block(); - nmos::details::nc_oid oid{ 2 }; + nmos::nc_oid oid = nmos::root_block_oid; + // example device manager - auto device_manager = nmos::make_device_manager(oid++, root_block, model.settings); + auto device_manager = nmos::make_device_manager(++oid, model.settings); // example class manager - auto class_manager = nmos::make_class_manager(oid++, root_block, control_protocol_state); + auto class_manager = nmos::make_class_manager(++oid, control_protocol_state); // example stereo gain - const auto& root_block_oid = nmos::fields::nc::oid(root_block.data); - const auto stereo_gain_oid = oid++; - // add master-gain and channel-gain - auto stereo_gain = nmos::make_block(stereo_gain_oid, root_block_oid, U("stereo-gain"), U("Stereo gain")); + const auto stereo_gain_oid = ++oid; + auto stereo_gain = nmos::make_block(stereo_gain_oid, nmos::root_block_oid, U("stereo-gain"), U("Stereo gain")); // example channel gain - const auto channel_gain_oid = oid++; + const auto channel_gain_oid = ++oid; + auto channel_gain = nmos::make_block(channel_gain_oid, stereo_gain_oid, U("channel-gain"), U("Channel gain")); // example left/right gains - auto left_gain = make_gain_control(oid++, channel_gain_oid, U("left-gain"), U("Left gain")); - auto right_gain = make_gain_control(oid++, channel_gain_oid, U("right-gain"), U("Right gain")); + auto left_gain = make_gain_control(++oid, channel_gain_oid, U("left-gain"), U("Left gain")); + auto right_gain = make_gain_control(++oid, channel_gain_oid, U("right-gain"), U("Right gain")); // add left-gain and right-gain to channel gain - auto channel_gain = nmos::make_block(channel_gain_oid, stereo_gain_oid, U("channel-gain"), U("Channel gain")); - nmos::add_member_to_block(U("Left channel gain"), left_gain.data, channel_gain.data); - nmos::add_member_to_block(U("Right channel gain"), right_gain.data, channel_gain.data); + nmos::add_member(U("Left channel gain"), left_gain, channel_gain); + nmos::add_member(U("Right channel gain"), right_gain, channel_gain); // example master-gain - auto master_gain = make_gain_control(oid++, channel_gain_oid, U("master-gain"), U("Master gain")); + auto master_gain = make_gain_control(++oid, channel_gain_oid, U("master-gain"), U("Master gain")); // add master-gain and channel-gain to stereo-gain - nmos::add_member_to_block(U("Master gain block"), master_gain.data, stereo_gain.data); - nmos::add_member_to_block(U("Channel gain block"), channel_gain.data, stereo_gain.data); + nmos::add_member(U("Master gain block"), master_gain, stereo_gain); + nmos::add_member(U("Channel gain block"), channel_gain, stereo_gain); + // add stereo-gain to root-block - nmos::add_member_to_block(U("Stereo gain block"), stereo_gain.data, root_block.data); + nmos::add_member(U("Stereo gain block"), stereo_gain, root_block); + // add class-manager to root-block + nmos::add_member(U("The class manager offers access to control class and data type descriptors"), class_manager, root_block); + // add device-manager to root-block + nmos::add_member(U("The device manager offers information about the product this device is representing"), device_manager, root_block); // insert resources to model if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(left_gain), gate)) throw node_implementation_init_exception(); diff --git a/Development/nmos/control_protocol_class_id.cpp b/Development/nmos/control_protocol_class_id.cpp deleted file mode 100644 index 8ca0ec90c..000000000 --- a/Development/nmos/control_protocol_class_id.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "nmos/control_protocol_class_id.h" - -namespace nmos -{ - namespace details - { - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid - web::json::value make_nc_class_id(const nc_class_id& class_id) - { - using web::json::value; - - auto nc_class_id = value::array(); - for (const auto class_id_item : class_id) { web::json::push_back(nc_class_id, class_id_item); } - return nc_class_id; - } - - nc_class_id parse_nc_class_id(const web::json::array& class_id_) - { - nc_class_id class_id; - for (auto& element : class_id_) - { - class_id.push_back(element.as_integer()); - } - return class_id; - } - } -} diff --git a/Development/nmos/control_protocol_class_id.h b/Development/nmos/control_protocol_class_id.h deleted file mode 100644 index f48bbe731..000000000 --- a/Development/nmos/control_protocol_class_id.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef NMOS_CONTROL_PROTOCOL_CLASS_ID_H -#define NMOS_CONTROL_PROTOCOL_CLASS_ID_H - -#include "cpprest/json_utils.h" - -namespace nmos -{ - namespace details - { - // see https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid - typedef std::vector nc_class_id; - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid - web::json::value make_nc_class_id(const nc_class_id& class_id); - nc_class_id parse_nc_class_id(const web::json::array& class_id); - } -} - -#endif diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index 5e52f0700..1118a35b8 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -1,5 +1,6 @@ #include "nmos/control_protocol_handlers.h" +#include "nmos/control_protocol_resource.h" #include "nmos/slog.h" namespace nmos @@ -18,7 +19,7 @@ namespace nmos add_control_protocol_class_handler make_add_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) { - return [&](const details::nc_class_id& class_id, const experimental::control_class& control_class) + return [&](const nc_class_id& class_id, const experimental::control_class& control_class) { slog::log(gate, SLOG_FLF) << "Add control class to cache"; diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index 565bf6c26..165b53809 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -23,7 +23,7 @@ namespace nmos // callback to add user control protocol class // this callback should not throw exceptions - typedef std::function add_control_protocol_class_handler; + typedef std::function add_control_protocol_class_handler; // callback to retrieve all control protocol datatypes // this callback should not throw exceptions diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 731dc00d8..4ab09f694 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -31,95 +31,57 @@ namespace nmos return result; } - web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::handle, handle }, - { nmos::fields::nc::result, make_nc_method_result_error(method_result, error_message) } - }); - } - - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::handle, handle }, - { nmos::fields::nc::result, make_nc_method_result(method_result) } - }); - } - - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value) + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid + web::json::value make_nc_element_id(uint16_t level, uint16_t index) { using web::json::value_of; return value_of({ - { nmos::fields::nc::handle, handle }, - { nmos::fields::nc::result, make_nc_method_result(method_result, value) } + { nmos::fields::nc::level, level }, + { nmos::fields::nc::index, index } }); } - - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, uint32_t value_) + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid + web::json::value make_nc_element_id(const nc_element_id& element_id) { - using web::json::value; - - return make_control_protocol_response(handle, method_result, value(value_)); + return make_nc_element_id(element_id.level, element_id.index); } - // message response - // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-message-type - web::json::value make_control_protocol_message_response(nc_message_type::type type, const web::json::value& responses) + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventid + web::json::value make_nc_event_id(const nc_event_id& id) { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::message_type, type }, - { nmos::fields::nc::responses, responses } - }); + return make_nc_element_id(id); } - // error message - // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#error-messages - web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message) + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodid + web::json::value make_nc_method_id(const nc_method_id& id) { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::message_type, nc_message_type::error }, - { nmos::fields::nc::status, method_result.status}, - { nmos::fields::nc::error_message, error_message } - }); + return make_nc_element_id(id); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid - web::json::value make_nc_element_id(uint16_t level, uint16_t index) + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertyid + web::json::value make_nc_property_id(const nc_property_id& id) { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::level, level }, - { nmos::fields::nc::index, index } - }); + return make_nc_element_id(id); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventid - web::json::value make_nc_event_id(uint16_t level, uint16_t index) + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid + web::json::value make_nc_class_id(const nc_class_id& class_id) { - return make_nc_element_id(level, index); - } + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodid - web::json::value make_nc_method_id(uint16_t level, uint16_t index) - { - return make_nc_element_id(level, index); + auto nc_class_id = value::array(); + for (const auto class_id_item : class_id) { web::json::push_back(nc_class_id, class_id_item); } + return nc_class_id; } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertyid - web::json::value make_nc_property_id(uint16_t level, uint16_t index) + nc_class_id parse_nc_class_id(const web::json::array& class_id_) { - return make_nc_element_id(level, index); + nc_class_id class_id; + for (auto& element : class_id_) + { + class_id.push_back(element.as_integer()); + } + return class_id; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanufacturer @@ -177,7 +139,7 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblockmemberdescriptor // description can be null // user_label can be null - web::json::value make_nc_block_member_descriptor(const web::json::value& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const web::json::value& class_id, const web::json::value& user_label, nc_oid owner) + web::json::value make_nc_block_member_descriptor(const web::json::value& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const web::json::value& user_label, nc_oid owner) { using web::json::value; @@ -185,12 +147,18 @@ namespace nmos data[nmos::fields::nc::role] = value::string(role); data[nmos::fields::nc::oid] = oid; data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); - data[nmos::fields::nc::class_id] = class_id; + data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); data[nmos::fields::nc::user_label] = user_label; data[nmos::fields::nc::owner] = owner; return data; } + web::json::value make_nc_block_member_descriptor(const utility::string_t& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const utility::string_t& user_label, nc_oid owner) + { + using web::json::value; + + return make_nc_block_member_descriptor(value::string(description), role, oid, constant_oid, class_id, value::string(user_label), owner); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassdescriptor // description can be null @@ -209,6 +177,12 @@ namespace nmos return data; } + web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) + { + using web::json::value; + + return make_nc_class_descriptor(value::string(description), class_id, name, fixed_role, properties, methods, events); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncenumitemdescriptor // description can be null @@ -222,22 +196,34 @@ namespace nmos return data; } + web::json::value make_nc_enum_item_descriptor(const utility::string_t& description, const utility::string_t& name, uint16_t val) + { + using web::json::value; + + return make_nc_enum_item_descriptor(value::string(description), name, val); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventdescriptor // description can be null // id = make_nc_event_id(level, index) - web::json::value make_nc_event_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const utility::string_t& event_datatype, bool is_deprecated) + web::json::value make_nc_event_descriptor(const web::json::value& description, const nc_event_id& id, const utility::string_t& name, const utility::string_t& event_datatype, bool is_deprecated) { using web::json::value; auto data = make_nc_descriptor(description); - data[nmos::fields::nc::id] = id; + data[nmos::fields::nc::id] = make_nc_event_id(id); data[nmos::fields::nc::name] = value::string(name); data[nmos::fields::nc::event_datatype] = value::string(event_datatype); data[nmos::fields::nc::is_deprecated] = value::boolean(is_deprecated); return data; } + web::json::value make_nc_event_descriptor(const utility::string_t& description, const nc_event_id& id, const utility::string_t& name, const utility::string_t& event_datatype, bool is_deprecated) + { + using web::json::value; + + return make_nc_event_descriptor(value::string(description), id, name, event_datatype, is_deprecated); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncfielddescriptor // description can be null @@ -256,17 +242,23 @@ namespace nmos return data; } + web::json::value make_nc_field_descriptor(const utility::string_t& description, const utility::string_t& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + { + using web::json::value; + + return make_nc_field_descriptor(value::string(description), name, value::string(type_name), is_nullable, is_sequence, constraints); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethoddescriptor // description can be null // id = make_nc_method_id(level, index) // sequence parameters - web::json::value make_nc_method_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) + web::json::value make_nc_method_descriptor(const web::json::value& description, const nc_method_id& id, const utility::string_t& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) { using web::json::value; auto data = make_nc_descriptor(description); - data[nmos::fields::nc::id] = id; + data[nmos::fields::nc::id] = make_nc_method_id(id); data[nmos::fields::nc::name] = value::string(name); data[nmos::fields::nc::result_datatype] = value::string(result_datatype); data[nmos::fields::nc::parameters] = parameters; @@ -274,6 +266,12 @@ namespace nmos return data; } + web::json::value make_nc_method_descriptor(const utility::string_t& description, const nc_method_id& id, const utility::string_t& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) + { + using web::json::value; + + return make_nc_method_descriptor(value::string(description), id, name, result_datatype, parameters, is_deprecated); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncparameterdescriptor // description can be null @@ -291,19 +289,29 @@ namespace nmos return data; } + web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const utility::string_t& name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + { + using web::json::value; + + return make_nc_parameter_descriptor(value::string(description), name, value::null(), is_nullable, is_sequence, constraints); + } + web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const utility::string_t& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + { + using web::json::value; + + return make_nc_parameter_descriptor(value::string(description), name, value::string(type_name), is_nullable, is_sequence, constraints); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertydescriptor // description can be null - // id = make_nc_property_id(level, index); - // type_name can be null // constraints can be null - web::json::value make_nc_property_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const web::json::value& type_name, + web::json::value make_nc_property_descriptor(const web::json::value& description, const nc_property_id& id, const utility::string_t& name, const web::json::value& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) { using web::json::value; auto data = make_nc_descriptor(description); - data[nmos::fields::nc::id] = id; + data[nmos::fields::nc::id] = make_nc_property_id(id); data[nmos::fields::nc::name] = value::string(name); data[nmos::fields::nc::type_name] = type_name; data[nmos::fields::nc::is_read_only] = value::boolean(is_read_only); @@ -314,6 +322,13 @@ namespace nmos return data; } + web::json::value make_nc_property_descriptor(const utility::string_t& description, const nc_property_id& id, const utility::string_t& name, const utility::string_t& type_name, + bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) + { + using web::json::value; + + return nmos::details::make_nc_property_descriptor(value::string(description), id, name, value::string(type_name), is_read_only, is_nullable, is_sequence, is_deprecated, constraints); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptor // description can be null @@ -379,1128 +394,1214 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject - web::json::value make_nc_object_properties() - { - using web::json::value; - - auto properties = value::array(); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Static value. All instances of the same class will have the same identity value")), make_nc_property_id(1, 1), nmos::fields::nc::class_id, value::string(U("NcClassId")), true, false, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Object identifier")), make_nc_property_id(1, 2), nmos::fields::nc::oid, value::string(U("NcOid")), true, false, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("TRUE iff OID is hardwired into device")), make_nc_property_id(1, 3), nmos::fields::nc::constant_oid, value::string(U("NcBoolean")), true, false, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("OID of containing block. Can only ever be null for the root block")), make_nc_property_id(1, 4), nmos::fields::nc::owner, value::string(U("NcOid")), true, true, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Role of object in the containing block")), make_nc_property_id(1, 5), nmos::fields::nc::role, value::string(U("NcString")), true, false, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Scribble strip")), make_nc_property_id(1, 6), nmos::fields::nc::user_label, value::string(U("NcString")), false, true, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Touchpoints to other contexts")), make_nc_property_id(1, 7), nmos::fields::nc::touchpoints, value::string(U("NcTouchpoint")), true, true, true, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Runtime property constraints")), make_nc_property_id(1, 8), nmos::fields::nc::runtime_property_constraints, value::string(U("NcPropertyConstraints")), true, true, true, false)); - - return properties; - } - web::json::value make_nc_object_methods() - { - using web::json::value; - - auto methods = value::array(); - { - auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); - web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Get property value")), make_nc_method_id(1, 1), U("Get"), U("NcMethodResultPropertyValue"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property value")), nmos::fields::nc::value, value::null(), true, false)); - web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Set property value")), make_nc_method_id(1, 2), U("Set"), U("NcMethodResult"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false)); - web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Get sequence item")), make_nc_method_id(1, 3), U("GetSequenceItem"), U("NcMethodResultPropertyValue"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false)); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Value")), nmos::fields::nc::value, value::null(), true, false)); - web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Set sequence item value")), make_nc_method_id(1, 4), U("SetSequenceItem"), U("NcMethodResult"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Value")), nmos::fields::nc::value, value::null(), true, false)); - web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Add item to sequence")), make_nc_method_id(1, 5), U("AddSequenceItem"), U("NcMethodResultId"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Index of item in the sequence")), nmos::fields::nc::index, value::string(U("NcId")), false, false)); - web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Delete sequence item")), make_nc_method_id(1, 6), U("RemoveSequenceItem"), U("NcMethodResult"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Property id")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); - web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Get sequence length")), make_nc_method_id(1, 7), U("GetSequenceLength"), U("NcMethodResultLength"), parameters, false)); - } - - return methods; - } - web::json::value make_nc_object_events() + web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { using web::json::value; - auto events = value::array(); - web::json::push_back(events, make_nc_event_descriptor(value::string(U("Property changed event")), make_nc_event_id(1, 1), U("PropertyChanged"), U("NcPropertyChangedEventData"), false)); + const auto id = utility::conversions::details::to_string_t(oid); +// auto data = make_resource_core(id, user_label.is_null() ? U("") : user_label.as_string(), {}); + value data; + data[nmos::fields::id] = value::string(id); // required for nmos::resource + data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); + data[nmos::fields::nc::oid] = oid; + data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); + data[nmos::fields::nc::owner] = owner; + data[nmos::fields::nc::role] = value::string(role); + data[nmos::fields::nc::user_label] = user_label; + data[nmos::fields::nc::touchpoints] = touchpoints; + data[nmos::fields::nc::runtime_property_constraints] = runtime_property_constraints; - return events; + return data; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock - web::json::value make_nc_block_properties() - { - using web::json::value; - - auto properties = value::array(); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("TRUE if block is functional")), make_nc_property_id(2, 1), nmos::fields::nc::enabled, value::string(U("NcBoolean")), true, false, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Descriptors of this block's members")), make_nc_property_id(2, 2), nmos::fields::nc::members, value::string(U("NcBlockMemberDescriptor")), true, false, true, false)); - - return properties; - } - web::json::value make_nc_block_methods() + web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members) { using web::json::value; - auto methods = value::array(); - { - auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("If recurse is set to true, nested members can be retrieved")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false)); - web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Gets descriptors of members of the block")), make_nc_method_id(2, 1), U("GetMemberDescriptors"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Relative path to search for (MUST not include the role of the block targeted by oid)")), nmos::fields::nc::path, value::string(U("NcRolePath")), false, false)); - web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Finds member(s) by path")), make_nc_method_id(2, 2), U("FindMembersByPath"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Role text to search for")), nmos::fields::nc::role, value::string(U("NcString")), false, false)); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("Signals if the comparison should be case sensitive")), nmos::fields::nc::case_sensitive, value::string(U("NcBoolean")), false, false)); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("TRUE to only return exact matches")), nmos::fields::nc::match_whole_string, value::string(U("NcBoolean")), false, false)); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("TRUE to search nested blocks")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false)); - web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Finds members with given role name or fragment")), make_nc_method_id(2, 3), U("FindMembersByRole"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("Class id to search for")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false)); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("If TRUE it will also include derived class descriptors")), nmos::fields::nc::include_derived, value::string(U("NcBoolean")), false, false)); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(value::string(U("TRUE to search nested blocks")), nmos::fields::nc::recurse, value::string(U("NcBoolean")), false, false)); - web::json::push_back(methods, details::make_nc_method_descriptor(value::string(U("Finds members with given class id")), details::make_nc_method_id(2, 4), U("FindMembersByClassId"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); - } - - return methods; - } - web::json::value make_nc_block_events() - { - using web::json::value; + auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); + data[nmos::fields::nc::enabled] = value::boolean(enabled); + data[nmos::fields::nc::members] = members; - return value::array(); + return data; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker - web::json::value make_nc_worker_properties() - { - using web::json::value; - - auto properties = value::array(); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("TRUE iff worker is enabled")), make_nc_property_id(2, 1), nmos::fields::nc::enabled, value::string(U("NcBoolean")), false, false, false, false)); - - return properties; - } - web::json::value make_nc_worker_methods() + web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled) { using web::json::value; - return value::array(); - } - web::json::value make_nc_worker_events() - { - using web::json::value; + auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); + data[nmos::fields::nc::enabled] = value::boolean(enabled); - return value::array(); + return data; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager - web::json::value make_nc_manager_properties() - { - using web::json::value; - - return value::array(); - } - web::json::value make_nc_manager_methods() - { - using web::json::value; - - return value::array(); - } - web::json::value make_nc_manager_events() + web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { - using web::json::value; - - return value::array(); + return make_nc_object(class_id, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager - web::json::value make_nc_device_manager_properties() - { - using web::json::value; - - auto properties = value::array(); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Version of MS-05-02 that this device uses")), make_nc_property_id(3, 1), nmos::fields::nc::nc_version, value::string(U("NcVersionCode")), true, false, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Manufacturer descriptor")), make_nc_property_id(3, 2), nmos::fields::nc::manufacturer, value::string(U("NcManufacturer")), true, false, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Product descriptor")), make_nc_property_id(3, 3), nmos::fields::nc::product, value::string(U("NcProduct")), true, false, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Serial number")), make_nc_property_id(3, 4), nmos::fields::nc::serial_number, value::string(U("NcString")), true, false, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Asset tracking identifier (user specified)")), make_nc_property_id(3, 5), nmos::fields::nc::user_inventory_code, value::string(U("NcString")), false, true, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Name of this device in the application. Instance name, not product name")), make_nc_property_id(3, 6), nmos::fields::nc::device_name, value::string(U("NcString")), false, true, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Role of this device in the application")), make_nc_property_id(3, 7), nmos::fields::nc::device_role, value::string(U("NcString")), false, true, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Device operational state")), make_nc_property_id(3, 8), nmos::fields::nc::operational_state, value::string(U("NcDeviceOperationalState")), true, false, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Reason for most recent reset")), make_nc_property_id(3, 9), nmos::fields::nc::reset_cause, value::string(U("NcResetCause")), true, false, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Arbitrary message from dev to controller")), make_nc_property_id(3, 10), nmos::fields::nc::message, value::string(U("NcString")), true, true, false, false)); - - return properties; - } - web::json::value make_nc_device_manager_methods() + web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, + const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, + const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause) { using web::json::value; - return value::array(); - } - web::json::value make_nc_device_manager_events() - { - using web::json::value; + auto data = details::make_nc_manager(nc_device_manager_class_id, oid, true, owner, U("DeviceManager"), user_label, touchpoints, runtime_property_constraints); + data[nmos::fields::nc::nc_version] = value::string(U("v1.0.0")); + data[nmos::fields::nc::manufacturer] = manufacturer; + data[nmos::fields::nc::product] = product; + data[nmos::fields::nc::serial_number] = value::string(serial_number); + data[nmos::fields::nc::user_inventory_code] = user_inventory_code; + data[nmos::fields::nc::device_name] = device_name; + data[nmos::fields::nc::device_role] = device_role; + data[nmos::fields::nc::operational_state] = operational_state; + data[nmos::fields::nc::reset_cause] = reset_cause; + data[nmos::fields::nc::message] = value::null(); - return value::array(); + return data; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager_properties() + web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state) { using web::json::value; - auto properties = value::array(); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Descriptions of all control classes in the device (descriptors do not contain inherited elements)")), make_nc_property_id(3, 1), nmos::fields::nc::control_classes, value::string(U("NcClassDescriptor")), true, false, true, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Descriptions of all data types in the device (descriptors do not contain inherited elements)")), make_nc_property_id(3, 2), nmos::fields::nc::datatypes, value::string(U("NcDatatypeDescriptor")), true, false, true, false)); - - return properties; - } - web::json::value make_nc_class_manager_methods() - { - using web::json::value; + auto data = make_nc_manager(nc_class_manager_class_id, oid, true, owner, U("ClassManager"), user_label, touchpoints, runtime_property_constraints); - auto methods = value::array(); + // add control classes + data[nmos::fields::nc::control_classes] = value::array(); + auto& control_classes = data[nmos::fields::nc::control_classes]; + for (const auto& control_class : control_protocol_state.control_classes) { - auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("class ID")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false)); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("If set the descriptor would contain all inherited elements")), nmos::fields::nc::include_inherited, value::string(U("NcBoolean")), false, false)); - web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Get a single class descriptor")), make_nc_method_id(3, 1), U("GetControlClass"), U("NcMethodResultClassDescriptor"), parameters, false)); + auto& ctl_class = control_class.second; + web::json::push_back(control_classes, make_nc_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.fixed_role, ctl_class.properties, ctl_class.methods, ctl_class.events)); } + + // add datatypes + data[nmos::fields::nc::datatypes] = value::array(); + auto& datatypes = data[nmos::fields::nc::datatypes]; + for (const auto& datatype : control_protocol_state.datatypes) { - auto parameters = value::array(); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("name of datatype")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); - web::json::push_back(parameters, make_nc_parameter_descriptor(value::string(U("If set the descriptor would contain all inherited elements")), nmos::fields::nc::include_inherited, value::string(U("NcBoolean")), false, false)); - web::json::push_back(methods, make_nc_method_descriptor(value::string(U("Get a single datatype descriptor")), make_nc_method_id(3, 2), U("GetDatatype"), U("NcMethodResultDatatypeDescriptor"), parameters, false)); + web::json::push_back(datatypes, datatype.second.descriptor); } - return methods; + return data; } - web::json::value make_nc_class_manager_events() - { - using web::json::value; + } - return value::array(); - } + // message response + // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-message-type + web::json::value make_control_protocol_message_response(nc_message_type::type type, const web::json::value& responses) + { + using web::json::value_of; - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor - web::json::value make_nc_receiver_monitor_properties() - { - using web::json::value; + return value_of({ + { nmos::fields::nc::message_type, type }, + { nmos::fields::nc::responses, responses } + }); + } - auto properties = value::array(); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Connection status property")), make_nc_property_id(3, 1), nmos::fields::nc::connection_status, value::string(U("NcConnectionStatus")), true, false, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Connection status message property")), make_nc_property_id(3, 2), nmos::fields::nc::connection_status_message, value::string(U("NcString")), true, true, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Payload status property")), make_nc_property_id(3, 3), nmos::fields::nc::payload_status, value::string(U("NcPayloadStatus")), true, false, false, false)); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Payload status message property")), make_nc_property_id(3, 4), nmos::fields::nc::payload_status_message, value::string(U("NcString")), true, true, false, false)); + // error message + // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#error-messages + web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message) + { + using web::json::value_of; - return properties; - } - web::json::value make_nc_receiver_monitor_methods() - { - using web::json::value; + return value_of({ + { nmos::fields::nc::message_type, nc_message_type::error }, + { nmos::fields::nc::status, method_result.status}, + { nmos::fields::nc::error_message, error_message } + }); + } - return value::array(); - } - web::json::value make_nc_receiver_monitor_events() - { - using web::json::value; + web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message) + { + using web::json::value_of; - return value::array(); - } + return value_of({ + { nmos::fields::nc::handle, handle }, + { nmos::fields::nc::result, details::make_nc_method_result_error(method_result, error_message) } + }); + } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitorprotected - web::json::value make_nc_receiver_monitor_protected_properties() - { - using web::json::value; + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result) + { + using web::json::value_of; - auto properties = value::array(); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Indicates if signal protection is active")), make_nc_property_id(4, 1), nmos::fields::nc::signal_protection_status, value::string(U("NcBoolean")), true, false, false, false)); + return value_of({ + { nmos::fields::nc::handle, handle }, + { nmos::fields::nc::result, details::make_nc_method_result(method_result) } + }); + } - return properties; - } - web::json::value make_nc_receiver_monitor_protected_methods() - { - using web::json::value; + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value) + { + using web::json::value_of; - return value::array(); - } - web::json::value make_nc_receiver_monitor_protected_events() - { - using web::json::value; + return value_of({ + { nmos::fields::nc::handle, handle }, + { nmos::fields::nc::result, details::make_nc_method_result(method_result, value) } + }); + } - return value::array(); - } + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, uint32_t value_) + { + using web::json::value; - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon - web::json::value make_nc_ident_beacon_properties() - { - using web::json::value; + return make_control_protocol_response(handle, method_result, value(value_)); + } - auto properties = value::array(); - web::json::push_back(properties, make_nc_property_descriptor(value::string(U("Indicator active state")), make_nc_property_id(3, 1), nmos::fields::nc::active, value::string(U("NcBoolean")), false, false, false, false)); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject + web::json::value make_nc_object_properties() + { + using web::json::value; + + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Static value. All instances of the same class will have the same identity value"), { 1, 1 }, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Object identifier"), { 1, 2 }, nmos::fields::nc::oid, U("NcOid"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE iff OID is hardwired into device"), { 1, 3 }, nmos::fields::nc::constant_oid, U("NcBoolean"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("OID of containing block. Can only ever be null for the root block"), { 1, 4 }, nmos::fields::nc::owner, U("NcOid"), true, true, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Role of object in the containing block"), { 1, 5 }, nmos::fields::nc::role, U("NcString"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Scribble strip"), { 1, 6 }, nmos::fields::nc::user_label, U("NcString"), false, true, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Touchpoints to other contexts"), { 1, 7 }, nmos::fields::nc::touchpoints, U("NcTouchpoint"), true, true, true, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Runtime property constraints"), { 1, 8 }, nmos::fields::nc::runtime_property_constraints, U("NcPropertyConstraints"), true, true, true, false)); + + return properties; + } + web::json::value make_nc_object_methods() + { + using web::json::value; - return properties; + auto methods = value::array(); + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get property value"), { 1, 1 }, U("Get"), U("NcMethodResultPropertyValue"), parameters, false)); } - web::json::value make_nc_ident_beacon_methods() { - using web::json::value; - - return value::array(); + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property value"), nmos::fields::nc::value, true, false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Set property value"), { 1, 2 }, U("Set"), U("NcMethodResult"), parameters, false)); } - web::json::value make_nc_ident_beacon_events() { - using web::json::value; - - return value::array(); + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get sequence item"), { 1, 3 }, U("GetSequenceItem"), U("NcMethodResultPropertyValue"), parameters, false)); } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.html - web::json::value make_nc_object_class() { - using web::json::value; - - return make_nc_class_descriptor(value::string(U("NcObject class descriptor")), nc_object_class_id, U("NcObject"), value::null(), make_nc_object_properties(), make_nc_object_methods(), make_nc_object_events()); + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Set sequence item value"), { 1, 4 }, U("SetSequenceItem"), U("NcMethodResult"), parameters, false)); } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.1.html - web::json::value make_nc_block_class() { - using web::json::value; - - return make_nc_class_descriptor(value::string(U("NcBlock class descriptor")), nc_block_class_id, U("NcBlock"), value::null(), make_nc_block_properties(), make_nc_block_methods(), make_nc_block_events()); + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id,U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Add item to sequence"), { 1, 5 }, U("AddSequenceItem"), U("NcMethodResultId"), parameters, false)); } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.2.html - web::json::value make_nc_worker_class() { - using web::json::value; - - return make_nc_class_descriptor(value::string(U("NcWorker class descriptor")), nc_worker_class_id, U("NcWorker"), value::null(), make_nc_worker_properties(), make_nc_worker_methods(), make_nc_worker_events()); + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Delete sequence item"), { 1, 6 }, U("RemoveSequenceItem"), U("NcMethodResult"), parameters, false)); } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.html - web::json::value make_nc_manager_class() { - using web::json::value; - - return make_nc_class_descriptor(value::string(U("NcManager class descriptor")), nc_manager_class_id, U("NcManager"), value::null(), make_nc_manager_properties(), make_nc_manager_methods(), make_nc_manager_events()); + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get sequence length"), { 1, 7 }, U("GetSequenceLength"), U("NcMethodResultLength"), parameters, false)); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.1.html - web::json::value make_nc_device_manager_class() - { - using web::json::value; + return methods; + } + web::json::value make_nc_object_events() + { + using web::json::value; - return make_nc_class_descriptor(value::string(U("NcDeviceManager class descriptor")), nc_device_manager_class_id, U("NcDeviceManager"), value::string(U("DeviceManager")), make_nc_device_manager_properties(), make_nc_device_manager_methods(), make_nc_device_manager_events()); - } + auto events = value::array(); + web::json::push_back(events, details::make_nc_event_descriptor(U("Property changed event"), { 1, 1 }, U("PropertyChanged"), U("NcPropertyChangedEventData"), false)); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.2.html - web::json::value make_nc_class_manager_class() - { - using web::json::value; + return events; + } - return make_nc_class_descriptor(value::string(U("NcClassManager class descriptor")), nc_class_manager_class_id, U("NcClassManager"), value::string(U("ClassManager")), make_nc_class_manager_properties(), make_nc_class_manager_methods(), make_nc_class_manager_events()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + web::json::value make_nc_block_properties() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcBlockMemberDescriptor.html - web::json::value make_nc_block_member_descriptor_datatype() - { - using web::json::value; + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE if block is functional"), { 2, 1 }, nmos::fields::nc::enabled, U("NcBoolean"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptors of this block's members"), { 2, 2 }, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false)); - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Role of member in its containing block")), nmos::fields::nc::role, value::string(U("NcString")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("OID of member")), nmos::fields::nc::oid, value::string(U("NcOid")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff member's OID is hardwired into device")), nmos::fields::nc::constant_oid, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Class ID")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("User label")), nmos::fields::nc::user_label, value::string(U("NcString")), true, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Containing block's OID")), nmos::fields::nc::owner, value::string(U("NcOid")), false, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Descriptor which is specific to a block member")), U("NcBlockMemberDescriptor"), fields, value::string(U("NcDescriptor"))); - } + return properties; + } + web::json::value make_nc_block_methods() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcClassDescriptor.html - web::json::value make_nc_class_descriptor_datatype() + auto methods = value::array(); { - using web::json::value; - - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Identity of the class")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of the class")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Role if the class has fixed role (manager classes)")), nmos::fields::nc::fixed_role, value::string(U("NcString")), true, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Property descriptors")), nmos::fields::nc::properties, value::string(U("NcPropertyDescriptor")), false, true)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Method descriptors")), nmos::fields::nc::methods, value::string(U("NcMethodDescriptor")), false, true)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Event descriptors")), nmos::fields::nc::events, value::string(U("NcEventDescriptor")), false, true)); - return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class")), U("NcClassDescriptor"), fields, value::string(U("NcDescriptor"))); + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If recurse is set to true, nested members can be retrieved"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Gets descriptors of members of the block"), { 2, 1 }, U("GetMemberDescriptors"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcClassId.html - web::json::value make_nc_class_id_datatype() { - using web::json::value; - - return make_nc_datatype_typedef(value::string(U("Sequence of class ID fields")), U("NcClassId"), true, U("NcInt32")); + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Relative path to search for (MUST not include the role of the block targeted by oid)"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds member(s) by path"), { 2, 2 }, U("FindMembersByPath"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptor.html - web::json::value make_nc_datatype_descriptor_datatype() { - using web::json::value; - - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Datatype name")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Type: Primitive, Typedef, Struct, Enum")), nmos::fields::nc::type, value::string(U("NcDatatypeType")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Base datatype descriptor")), U("NcDatatypeDescriptor"), fields, value::string(U("NcDescriptor"))); + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Role text to search for"), nmos::fields::nc::role, U("NcString"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Signals if the comparison should be case sensitive"), nmos::fields::nc::case_sensitive, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("TRUE to only return exact matches"), nmos::fields::nc::match_whole_string, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("TRUE to search nested blocks"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds members with given role name or fragment"), { 2, 3 }, U("FindMembersByRole"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorEnum.html - web::json::value make_nc_datatype_descriptor_enum_datatype() { - using web::json::value; - - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("One item descriptor per enum option")), nmos::fields::nc::items, value::string(U("NcEnumItemDescriptor")), false, true)); - return make_nc_datatype_descriptor_struct(value::string(U("Enum datatype descriptor")), U("NcDatatypeDescriptorEnum"), fields, value::string(U("NcDatatypeDescriptor"))); + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Class id to search for"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If TRUE it will also include derived class descriptors"), nmos::fields::nc::include_derived, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("TRUE to search nested blocks"), nmos::fields::nc::recurse,U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds members with given class id"), { 2, 4 }, U("FindMembersByClassId"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorPrimitive.html - web::json::value make_nc_datatype_descriptor_primitive_datatype() - { - using web::json::value; + return methods; + } + web::json::value make_nc_block_events() + { + using web::json::value; - auto fields = value::array(); - return make_nc_datatype_descriptor_struct(value::string(U("Primitive datatype descriptor")), U("NcDatatypeDescriptorPrimitive"), fields, value::string(U("NcDatatypeDescriptor"))); - } + return value::array(); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorStruct.html - web::json::value make_nc_datatype_descriptor_struct_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker + web::json::value make_nc_worker_properties() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("One item descriptor per field of the struct")), nmos::fields::nc::fields, value::string(U("NcFieldDescriptor")), false, true)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of the parent type if any or null if it has no parent")), nmos::fields::nc::parent_type, value::string(U("NcName")), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Struct datatype descriptor")), U("NcDatatypeDescriptorStruct"), fields, value::string(U("NcDatatypeDescriptor"))); - } + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE iff worker is enabled"), { 2, 1 }, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false)); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorTypeDef.html - web::json::value make_nc_datatype_descriptor_type_def_datatype() - { - using web::json::value; + return properties; + } + web::json::value make_nc_worker_methods() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Original typedef datatype name")), nmos::fields::nc::parent_type, value::string(U("NcName")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff type is a typedef sequence of another type")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Type def datatype descriptor")), U("NcDatatypeDescriptorTypeDef"), fields, value::string(U("NcDatatypeDescriptor"))); - } + return value::array(); + } + web::json::value make_nc_worker_events() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeType.html - web::json::value make_nc_datatype_type_datatype() - { - using web::json::value; + return value::array(); + } - auto items = value::array(); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Primitive datatype")), U("Primitive"), 0)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Simple alias of another datatype")), U("Typedef"), 1)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Data structure")), U("Struct"), 2)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Enum datatype")), U("Enum"), 3)); - return make_nc_datatype_descriptor_enum(value::string(U("Datatype type")), U("NcDatatypeType"), items); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager + web::json::value make_nc_manager_properties() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDescriptor.html - web::json::value make_nc_descriptor_datatype() - { - using web::json::value; + return value::array(); + } + web::json::value make_nc_manager_methods() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional user facing description")), nmos::fields::nc::description, value::string(U("NcString")), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Base descriptor")), U("NcDescriptor"), fields, value::null()); - } + return value::array(); + } + web::json::value make_nc_manager_events() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDeviceGenericState.html - web::json::value make_nc_device_generic_state_datatype() - { - using web::json::value; + return value::array(); + } - auto items = value::array(); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Normal operation")), U("NormalOperation"), 1)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Device is initializing")), U("Initializing"), 2)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Device is performing a software or firmware update")), U("Updating"), 3)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Device is experiencing a licensing error")), U("LicensingError"), 4)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Device is experiencing an internal error")), U("InternalError"), 5)); - return make_nc_datatype_descriptor_enum(value::string(U("Device generic operational state")), U("NcDeviceGenericState"), items); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + web::json::value make_nc_device_manager_properties() + { + using web::json::value; + + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Version of MS-05-02 that this device uses"), { 3, 1 }, nmos::fields::nc::nc_version, U("NcVersionCode"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Manufacturer descriptor"), { 3, 2 }, nmos::fields::nc::manufacturer, U("NcManufacturer"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Product descriptor"), { 3, 3 }, nmos::fields::nc::product, U("NcProduct"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Serial number"), { 3, 4 }, nmos::fields::nc::serial_number, U("NcString"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Asset tracking identifier (user specified)"), { 3, 5 }, nmos::fields::nc::user_inventory_code, U("NcString"), false, true, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Name of this device in the application. Instance name, not product name"), { 3, 6 }, nmos::fields::nc::device_name, U("NcString"), false, true, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Role of this device in the application"), { 3, 7 }, nmos::fields::nc::device_role, U("NcString"), false, true, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Device operational state"), { 3, 8 }, nmos::fields::nc::operational_state, U("NcDeviceOperationalState"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Reason for most recent reset"), { 3, 9 }, nmos::fields::nc::reset_cause, U("NcResetCause"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Arbitrary message from dev to controller"), { 3, 10 }, nmos::fields::nc::message, U("NcString"), true, true, false, false)); + + return properties; + } + web::json::value make_nc_device_manager_methods() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDeviceOperationalState.html - web::json::value make_nc_device_operational_state_datatype() - { - using web::json::value; + return value::array(); + } + web::json::value make_nc_device_manager_events() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Generic operational state")), nmos::fields::nc::generic_state, value::string(U("NcDeviceGenericState")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Specific device details")), nmos::fields::nc::device_specific_details, value::string(U("NcString")), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Device operational state")), U("NcDeviceOperationalState"), fields, value::null()); - } + return value::array(); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcElementId.html - web::json::value make_nc_element_id_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + web::json::value make_nc_class_manager_properties() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Level of the element")), nmos::fields::nc::level, value::string(U("NcUint16")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Index of the element")), nmos::fields::nc::index, value::string(U("NcUint16")), false, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Class element id which contains the level and index")), U("NcElementId"), fields, value::null()); - } + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptions of all control classes in the device (descriptors do not contain inherited elements)"), { 3, 1 }, nmos::fields::nc::control_classes, U("NcClassDescriptor"), true, false, true, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptions of all data types in the device (descriptors do not contain inherited elements)"), { 3, 2 }, nmos::fields::nc::datatypes, U("NcDatatypeDescriptor"), true, false, true, false)); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEnumItemDescriptor.html - web::json::value make_nc_enum_item_descriptor_datatype() - { - using web::json::value; + return properties; + } + web::json::value make_nc_class_manager_methods() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of option")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Enum item numerical value")), nmos::fields::nc::value, value::string(U("NcUint16")), false, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of an enum item")), U("NcEnumItemDescriptor"), fields, value::string(U("NcDescriptor"))); + auto methods = value::array(); + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("class ID"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::include_inherited, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get a single class descriptor"), { 3, 1 }, U("GetControlClass"), U("NcMethodResultClassDescriptor"), parameters, false)); } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEventDescriptor.html - web::json::value make_nc_event_descriptor_datatype() { - using web::json::value; - - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Event id with level and index")), nmos::fields::nc::id, value::string(U("NcEventId")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of event")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of event data's datatype")), nmos::fields::nc::event_datatype, value::string(U("NcName")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class event")), U("NcEventDescriptor"), fields, value::string(U("NcDescriptor"))); + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("name of datatype"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::include_inherited, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get a single datatype descriptor"), { 3, 2 }, U("GetDatatype"), U("NcMethodResultDatatypeDescriptor"), parameters, false)); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEventId.html - web::json::value make_nc_event_id_datatype() - { - using web::json::value; + return methods; + } + web::json::value make_nc_class_manager_events() + { + using web::json::value; - return make_nc_datatype_descriptor_struct(value::string(U("Event id which contains the level and index")), U("NcEventId"), value::array(), value::string(U("NcElementId"))); - } + return value::array(); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcFieldDescriptor.html - web::json::value make_nc_field_descriptor_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor + web::json::value make_nc_receiver_monitor_properties() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of field")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of field's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff field is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff field is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a field of a struct")), U("NcFieldDescriptor"), fields, value::string(U("NcDescriptor"))); - } + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status property"), { 3, 1 }, nmos::fields::nc::connection_status, U("NcConnectionStatus"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status message property"), { 3, 2 }, nmos::fields::nc::connection_status_message, U("NcString"), true, true, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Payload status property"), { 3, 3 }, nmos::fields::nc::payload_status, U("NcPayloadStatus"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Payload status message property"), { 3, 4 }, nmos::fields::nc::payload_status_message, U("NcString"), true, true, false, false)); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcId.html - web::json::value make_nc_id_datatype() - { - using web::json::value; + return properties; + } + web::json::value make_nc_receiver_monitor_methods() + { + using web::json::value; - return make_nc_datatype_typedef(value::string(U("Identity handler")), U("NcId"), false, U("NcUint32")); - } + return value::array(); + } + web::json::value make_nc_receiver_monitor_events() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcManufacturer.html - web::json::value make_nc_manufacturer_datatype() - { - using web::json::value; + return value::array(); + } - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Manufacturer's name")), nmos::fields::nc::name, value::string(U("NcString")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("IEEE OUI or CID of manufacturer")), nmos::fields::nc::organization_id, value::string(U("NcOrganizationId")), true, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("URL of the manufacturer's website")), nmos::fields::nc::website, value::string(U("NcUri")), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Manufacturer descriptor")), U("NcManufacturer"), fields, value::null()); - } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitorprotected + web::json::value make_nc_receiver_monitor_protected_properties() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodDescriptor.html - web::json::value make_nc_method_descriptor_datatype() - { - using web::json::value; + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Indicates if signal protection is active"), { 4, 1 }, nmos::fields::nc::signal_protection_status, U("NcBoolean"), true, false, false, false)); - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Method id with level and index")), nmos::fields::nc::id, value::string(U("NcMethodId")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of method")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of method result's datatype")), nmos::fields::nc::result_datatype, value::string(U("NcName")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Parameter descriptors if any")), nmos::fields::nc::parameters, value::string(U("NcParameterDescriptor")), false, true)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class method")), U("NcMethodDescriptor"), fields, value::string(U("NcDescriptor"))); - } + return properties; + } + web::json::value make_nc_receiver_monitor_protected_methods() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodId.html - web::json::value make_nc_method_id_datatype() - { - using web::json::value; + return value::array(); + } + web::json::value make_nc_receiver_monitor_protected_events() + { + using web::json::value; - return make_nc_datatype_descriptor_struct(value::string(U("Method id which contains the level and index")), U("NcMethodId"), value::array(), value::string(U("NcElementId"))); - } + return value::array(); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResult.html - web::json::value make_nc_method_result_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon + web::json::value make_nc_ident_beacon_properties() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Status for the invoked method")), nmos::fields::nc::status, value::string(U("NcMethodStatus")), false, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Base result of the invoked method")), U("NcMethodResult"), fields, value::null()); - } + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Indicator active state"), { 3, 1 }, nmos::fields::nc::active, U("NcBoolean"), false, false, false, false)); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultBlockMemberDescriptors.html - web::json::value make_nc_method_result_block_member_descriptors_datatype() - { - using web::json::value; + return properties; + } + web::json::value make_nc_ident_beacon_methods() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Block member descriptors method result value")), nmos::fields::nc::value, value::string(U("NcBlockMemberDescriptor")), false, true)); - return make_nc_datatype_descriptor_struct(value::string(U("Method result containing block member descriptors as the value")), U("NcMethodResultBlockMemberDescriptors"), fields, value::string(U("NcMethodResult"))); - } + return value::array(); + } + web::json::value make_nc_ident_beacon_events() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultClassDescriptor.html - web::json::value make_nc_method_result_class_descriptor_datatype() - { - using web::json::value; + return value::array(); + } - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Class descriptor method result value")), nmos::fields::nc::value, value::string(U("NcClassDescriptor")), false, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Method result containing a class descriptor as the value")), U("NcMethodResultClassDescriptor"), fields, value::string(U("NcMethodResult"))); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.html + web::json::value make_nc_object_class() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultDatatypeDescriptor.html - web::json::value make_nc_method_result_datatype_descriptor_datatype() - { - using web::json::value; + return details::make_nc_class_descriptor(value::string(U("NcObject class descriptor")), nc_object_class_id, U("NcObject"), value::null(), make_nc_object_properties(), make_nc_object_methods(), make_nc_object_events()); + } - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Datatype descriptor method result value")), nmos::fields::nc::value, value::string(U("NcDatatypeDescriptor")), false, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Method result containing a datatype descriptor as the value")), U("NcMethodResultDatatypeDescriptor"), fields, value::string(U("NcMethodResult"))); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.1.html + web::json::value make_nc_block_class() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultError.html - web::json::value make_nc_method_result_error_datatype() - { - using web::json::value; + return details::make_nc_class_descriptor(value::string(U("NcBlock class descriptor")), nc_block_class_id, U("NcBlock"), value::null(), make_nc_block_properties(), make_nc_block_methods(), make_nc_block_events()); + } - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Error message")), nmos::fields::nc::error_message, value::string(U("NcString")), false, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Error result - to be used when the method call encounters an error")), U("NcMethodResultError"), fields, value::string(U("NcMethodResult"))); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.2.html + web::json::value make_nc_worker_class() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultId.html - web::json::value make_nc_method_result_id_datatype() - { - using web::json::value; + return details::make_nc_class_descriptor(value::string(U("NcWorker class descriptor")), nc_worker_class_id, U("NcWorker"), value::null(), make_nc_worker_properties(), make_nc_worker_methods(), make_nc_worker_events()); + } - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Id result value")), nmos::fields::nc::value, value::string(U("NcId")), false, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Id method result")), U("NcMethodResultId"), fields, value::string(U("NcMethodResult"))); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.html + web::json::value make_nc_manager_class() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultLength.html - web::json::value make_nc_method_result_length_datatype() - { - using web::json::value; + return details::make_nc_class_descriptor(value::string(U("NcManager class descriptor")), nc_manager_class_id, U("NcManager"), value::null(), make_nc_manager_properties(), make_nc_manager_methods(), make_nc_manager_events()); + } - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Length result value")), nmos::fields::nc::value, value::string(U("NcUint32")), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Length method result")), U("NcMethodResultLength"), fields, value::string(U("NcMethodResult"))); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.1.html + web::json::value make_nc_device_manager_class() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultPropertyValue.html - web::json::value make_nc_method_result_property_value_datatype() - { - using web::json::value; + return details::make_nc_class_descriptor(value::string(U("NcDeviceManager class descriptor")), nc_device_manager_class_id, U("NcDeviceManager"), value::string(U("DeviceManager")), make_nc_device_manager_properties(), make_nc_device_manager_methods(), make_nc_device_manager_events()); + } - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Getter method value for the associated property")), nmos::fields::nc::value, value::null(), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Result when invoking the getter method associated with a property")), U("NcMethodResultPropertyValue"), fields, value::string(U("NcMethodResult"))); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.2.html + web::json::value make_nc_class_manager_class() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodStatus.html - web::json::value make_nc_method_status_datatype() - { - using web::json::value; + return details::make_nc_class_descriptor(value::string(U("NcClassManager class descriptor")), nc_class_manager_class_id, U("NcClassManager"), value::string(U("ClassManager")), make_nc_class_manager_properties(), make_nc_class_manager_methods(), make_nc_class_manager_events()); + } - auto items = value::array(); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Method call was successful")), U("Ok"), 200)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Method call was successful but targeted property is deprecated")), U("PropertyDeprecated"), 298)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Method call was successful but method is deprecated")), U("MethodDeprecated"), 299)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Badly-formed command (e.g. the incoming command has invalid message encoding and cannot be parsed by the underlying protocol)")), U("BadCommandFormat"), 400)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Client is not authorized")), U("Unauthorized"), 401)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Command addresses a nonexistent object")), U("BadOid"), 404)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Attempt to change read-only state")), U("Readonly"), 405)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Method call is invalid in current operating context (e.g. attempting to invoke a method when the object is disabled)")), U("InvalidRequest"), 406)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("There is a conflict with the current state of the device")), U("Conflict"), 409)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Something was too big")), U("BufferOverflow"), 413)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Index is outside the available range")), U("IndexOutOfBounds"), 414)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Method parameter does not meet expectations (e.g. attempting to invoke a method with an invalid type for one of its parameters)")), U("ParameterError"), 417)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Addressed object is locked")), U("Locked"), 423)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Internal device error")), U("DeviceError"), 500)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Addressed method is not implemented by the addressed object")), U("MethodNotImplemented"), 501)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Addressed property is not implemented by the addressed object")), U("PropertyNotImplemented"), 502)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("The device is not ready to handle any commands")), U("NotReady"), 503)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Method call did not finish within the allotted time")), U("Timeout"), 504)); - return make_nc_datatype_descriptor_enum(value::string(U("Method invokation status")), U("NcMethodStatus"), items); - } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon + web::json::value make_nc_ident_beacon_class() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcName.html - web::json::value make_nc_name_datatype() - { - using web::json::value; + return details::make_nc_class_descriptor(value::string(U("NcIdentBeacon class descriptor")), nc_ident_beacon_class_id, U("NcIdentBeacon"), value::null(), make_nc_ident_beacon_properties(), make_nc_ident_beacon_methods(), make_nc_ident_beacon_events()); + } - return make_nc_datatype_typedef(value::string(U("Programmatically significant name, alphanumerics + underscore, no spaces")), U("NcName"), false, U("NcString")); - } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor + web::json::value make_nc_receiver_monitor_class() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcOid.html - web::json::value make_nc_oid_datatype() - { - using web::json::value; + return details::make_nc_class_descriptor(value::string(U("NcReceiverMonitor class descriptor")), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), value::null(), make_nc_receiver_monitor_properties(), make_nc_receiver_monitor_methods(), make_nc_receiver_monitor_events()); + } - return make_nc_datatype_typedef(value::string(U("Object id")), U("NcOid"), false, U("NcUint32")); - } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitorprotected + web::json::value make_nc_receiver_monitor_protected_class() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcOrganizationId.html - web::json::value make_nc_organization_id_datatype() - { - using web::json::value; + return details::make_nc_class_descriptor(value::string(U("NcReceiverMonitorProtected class descriptor")), nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), value::null(), make_nc_receiver_monitor_protected_properties(), make_nc_receiver_monitor_protected_methods(), make_nc_receiver_monitor_protected_events()); + } - return make_nc_datatype_typedef(value::string(U("Unique 24-bit organization id")), U("NcOrganizationId"), false, U("NcInt32")); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcBlockMemberDescriptor.html + web::json::value make_nc_block_member_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Role of member in its containing block")), nmos::fields::nc::role, value::string(U("NcString")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("OID of member")), nmos::fields::nc::oid, value::string(U("NcOid")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff member's OID is hardwired into device")), nmos::fields::nc::constant_oid, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Class ID")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("User label")), nmos::fields::nc::user_label, value::string(U("NcString")), true, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Containing block's OID")), nmos::fields::nc::owner, value::string(U("NcOid")), false, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor which is specific to a block member")), U("NcBlockMemberDescriptor"), fields, value::string(U("NcDescriptor"))); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraints.html - web::json::value make_nc_parameter_constraints_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcClassDescriptor.html + web::json::value make_nc_class_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Identity of the class")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of the class")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Role if the class has fixed role (manager classes)")), nmos::fields::nc::fixed_role, value::string(U("NcString")), true, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property descriptors")), nmos::fields::nc::properties, value::string(U("NcPropertyDescriptor")), false, true)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Method descriptors")), nmos::fields::nc::methods, value::string(U("NcMethodDescriptor")), false, true)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Event descriptors")), nmos::fields::nc::events, value::string(U("NcEventDescriptor")), false, true)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class")), U("NcClassDescriptor"), fields, value::string(U("NcDescriptor"))); + } - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Default value")), nmos::fields::nc::default_value, value::null(), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Abstract parameter constraints class")), U("NcParameterConstraints"), fields, value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcClassId.html + web::json::value make_nc_class_id_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraintsNumber.html - web::json::value make_nc_parameter_constraints_number_datatype() - { - using web::json::value; + return details::make_nc_datatype_typedef(value::string(U("Sequence of class ID fields")), U("NcClassId"), true, U("NcInt32")); + } - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional maximum")), nmos::fields::nc::maximum, value::null(), true, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional minimum")), nmos::fields::nc::minimum, value::null(), true, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional step")), nmos::fields::nc::step, value::null(), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Number parameter constraints class")), U("NcParameterConstraintsNumber"), fields, value::string(U("NcParameterConstraints"))); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptor.html + web::json::value make_nc_datatype_descriptor_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraintsString.html - web::json::value make_nc_parameter_constraints_string_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Datatype name")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Type: Primitive, Typedef, Struct, Enum")), nmos::fields::nc::type, value::string(U("NcDatatypeType")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Base datatype descriptor")), U("NcDatatypeDescriptor"), fields, value::string(U("NcDescriptor"))); + } - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Maximum characters allowed")), nmos::fields::nc::max_characters, value::string(U("NcUint32")), true, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Regex pattern")), nmos::fields::nc::pattern, value::string(U("NcRegex")), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("String parameter constraints class")), U("NcParameterConstraintsString"), fields, value::string(U("NcParameterConstraints"))); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorEnum.html + web::json::value make_nc_datatype_descriptor_enum_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterDescriptor.html - web::json::value make_nc_parameter_descriptor_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("One item descriptor per enum option")), nmos::fields::nc::items, value::string(U("NcEnumItemDescriptor")), false, true)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Enum datatype descriptor")), U("NcDatatypeDescriptorEnum"), fields, value::string(U("NcDatatypeDescriptor"))); + } - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of parameter")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of parameter's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a method parameter")), U("NcParameterDescriptor"), fields, value::string(U("NcDescriptor"))); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorPrimitive.html + web::json::value make_nc_datatype_descriptor_primitive_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcProduct.html - web::json::value make_nc_product_datatype() - { - using web::json::value; + auto fields = value::array(); + return details::make_nc_datatype_descriptor_struct(value::string(U("Primitive datatype descriptor")), U("NcDatatypeDescriptorPrimitive"), fields, value::string(U("NcDatatypeDescriptor"))); + } - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Product name")), nmos::fields::nc::name, value::string(U("NcString")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Manufacturer's unique key to product - model number, SKU, etc")), nmos::fields::nc::key, value::string(U("NcString")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Manufacturer's product revision level code")), nmos::fields::nc::revision_level, value::string(U("NcString")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Brand name under which product is sold")), nmos::fields::nc::brand_name, value::string(U("NcString")), true, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Unique UUID of product (not product instance)")), nmos::fields::nc::uuid, value::string(U("NcUuid")), true, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Text description of product")), nmos::fields::nc::description, value::string(U("NcString")), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Product descriptor")), U("NcProduct"), fields, value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorStruct.html + web::json::value make_nc_datatype_descriptor_struct_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyChangeType.html - web::json::value make_nc_property_change_type_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("One item descriptor per field of the struct")), nmos::fields::nc::fields, value::string(U("NcFieldDescriptor")), false, true)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of the parent type if any or null if it has no parent")), nmos::fields::nc::parent_type, value::string(U("NcName")), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Struct datatype descriptor")), U("NcDatatypeDescriptorStruct"), fields, value::string(U("NcDatatypeDescriptor"))); + } - auto items = value::array(); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Current value changed")), U("ValueChanged"), 0)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Sequence item added")), U("SequenceItemAdded"), 1)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Sequence item changed")), U("SequenceItemChanged"), 2)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Sequence item removed")), U("SequenceItemRemoved"), 3)); - return make_nc_datatype_descriptor_enum(value::string(U("Type of property change")), U("NcPropertyChangeType"), items); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorTypeDef.html + web::json::value make_nc_datatype_descriptor_type_def_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyChangedEventData.html - web::json::value make_nc_property_changed_event_data_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Original typedef datatype name")), nmos::fields::nc::parent_type, value::string(U("NcName")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff type is a typedef sequence of another type")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Type def datatype descriptor")), U("NcDatatypeDescriptorTypeDef"), fields, value::string(U("NcDatatypeDescriptor"))); + } - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("The id of the property that changed")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Information regarding the change type")), nmos::fields::nc::change_type, value::string(U("NcPropertyChangeType")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Property-type specific value")), nmos::fields::nc::value, value::null(), true, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Index of sequence item if the property is a sequence")), nmos::fields::nc::sequence_item_index, value::string(U("NcId")), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Payload of property-changed event")), U("NcPropertyChangedEventData"), fields, value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeType.html + web::json::value make_nc_datatype_type_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Primitive datatype")), U("Primitive"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Simple alias of another datatype")), U("Typedef"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Data structure")), U("Struct"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Enum datatype")), U("Enum"), 3)); + return details::make_nc_datatype_descriptor_enum(value::string(U("Datatype type")), U("NcDatatypeType"), items); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraints.html - web::json::value make_nc_property_contraints_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDescriptor.html + web::json::value make_nc_descriptor_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("The id of the property being constrained")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional default value")), nmos::fields::nc::default_value, value::null(), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Property constraints class")), U("NcPropertyConstraints"), fields, value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional user facing description")), nmos::fields::nc::description, value::string(U("NcString")), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Base descriptor")), U("NcDescriptor"), fields, value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraintsNumber.html - web::json::value make_nc_property_constraints_number_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDeviceGenericState.html + web::json::value make_nc_device_generic_state_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Normal operation")), U("NormalOperation"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is initializing")), U("Initializing"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is performing a software or firmware update")), U("Updating"), 3)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is experiencing a licensing error")), U("LicensingError"), 4)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is experiencing an internal error")), U("InternalError"), 5)); + return details::make_nc_datatype_descriptor_enum(value::string(U("Device generic operational state")), U("NcDeviceGenericState"), items); + } - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional maximum")), nmos::fields::nc::maximum, value::null(), true, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional minimum")), nmos::fields::nc::minimum, value::null(), true, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional step")), nmos::fields::nc::step, value::null(), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Number property constraints class")), U("NcPropertyConstraintsNumber"), fields, value::string(U("NcPropertyConstraints"))); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDeviceOperationalState.html + web::json::value make_nc_device_operational_state_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraintsString.html - web::json::value make_nc_property_constraints_string_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Generic operational state")), nmos::fields::nc::generic_state, value::string(U("NcDeviceGenericState")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Specific device details")), nmos::fields::nc::device_specific_details, value::string(U("NcString")), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Device operational state")), U("NcDeviceOperationalState"), fields, value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Maximum characters allowed")), nmos::fields::nc::max_characters, value::string(U("NcUint32")), true, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Regex pattern")), nmos::fields::nc::pattern, value::string(U("NcRegex")), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("String property constraints class")), U("NcPropertyConstraintsString"), fields, value::string(U("NcPropertyConstraints"))); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcElementId.html + web::json::value make_nc_element_id_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyDescriptor.html - web::json::value make_nc_property_descriptor_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Level of the element")), nmos::fields::nc::level, value::string(U("NcUint16")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Index of the element")), nmos::fields::nc::index, value::string(U("NcUint16")), false, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Class element id which contains the level and index")), U("NcElementId"), fields, value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Property id with level and index")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of property")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Name of property's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is read-only")), nmos::fields::nc::is_read_only, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class property")), U("NcPropertyDescriptor"), fields, value::string(U("NcDescriptor"))); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEnumItemDescriptor.html + web::json::value make_nc_enum_item_descriptor_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyId.html - web::json::value make_nc_property_id_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of option")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Enum item numerical value")), nmos::fields::nc::value, value::string(U("NcUint16")), false, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of an enum item")), U("NcEnumItemDescriptor"), fields, value::string(U("NcDescriptor"))); + } - return make_nc_datatype_descriptor_struct(value::string(U("Property id which contains the level and index")), U("NcPropertyId"), value::array(), value::string(U("NcElementId"))); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEventDescriptor.html + web::json::value make_nc_event_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Event id with level and index")), nmos::fields::nc::id, value::string(U("NcEventId")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of event")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of event data's datatype")), nmos::fields::nc::event_datatype, value::string(U("NcName")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class event")), U("NcEventDescriptor"), fields, value::string(U("NcDescriptor"))); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcRegex.html - web::json::value make_nc_regex_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEventId.html + web::json::value make_nc_event_id_datatype() + { + using web::json::value; - return make_nc_datatype_typedef(value::string(U("Regex pattern")), U("NcRegex"), false, U("NcString")); - } + return details::make_nc_datatype_descriptor_struct(value::string(U("Event id which contains the level and index")), U("NcEventId"), value::array(), value::string(U("NcElementId"))); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcResetCause.html - web::json::value make_nc_reset_cause_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcFieldDescriptor.html + web::json::value make_nc_field_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of field")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of field's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff field is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff field is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a field of a struct")), U("NcFieldDescriptor"), fields, value::string(U("NcDescriptor"))); + } - auto items = value::array(); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Power on")), U("PowerOn"), 1)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Internal error")), U("InternalError"), 2)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Upgrade")), U("Upgrade"), 3)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Controller request")), U("ControllerRequest"), 4)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Manual request from the front panel")), U("ManualReset"), 5)); - return make_nc_datatype_descriptor_enum(value::string(U("Reset cause enum")), U("NcResetCause"), items); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcId.html + web::json::value make_nc_id_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcRolePath.html - web::json::value make_nc_role_path_datatype() - { - using web::json::value; + return details::make_nc_datatype_typedef(value::string(U("Identity handler")), U("NcId"), false, U("NcUint32")); + } - return make_nc_datatype_typedef(value::string(U("Role path")), U("NcRolePath"), true, U("NcString")); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcManufacturer.html + web::json::value make_nc_manufacturer_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTimeInterval.html - web::json::value make_nc_time_interval_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's name")), nmos::fields::nc::name, value::string(U("NcString")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("IEEE OUI or CID of manufacturer")), nmos::fields::nc::organization_id, value::string(U("NcOrganizationId")), true, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("URL of the manufacturer's website")), nmos::fields::nc::website, value::string(U("NcUri")), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Manufacturer descriptor")), U("NcManufacturer"), fields, value::null()); + } - return make_nc_datatype_typedef(value::string(U("Time interval described in nanoseconds")), U("NcTimeInterval"), false, U("NcInt64")); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodDescriptor.html + web::json::value make_nc_method_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Method id with level and index")), nmos::fields::nc::id, value::string(U("NcMethodId")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of method")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of method result's datatype")), nmos::fields::nc::result_datatype, value::string(U("NcName")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Parameter descriptors if any")), nmos::fields::nc::parameters, value::string(U("NcParameterDescriptor")), false, true)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class method")), U("NcMethodDescriptor"), fields, value::string(U("NcDescriptor"))); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpoint.html - web::json::value make_nc_touchpoint_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodId.html + web::json::value make_nc_method_id_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Context namespace")), nmos::fields::nc::context_namespace, value::string(U("NcString")), false, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Base touchpoint class")), U("NcTouchpoint"), fields, value::null()); - } + return details::make_nc_datatype_descriptor_struct(value::string(U("Method id which contains the level and index")), U("NcMethodId"), value::array(), value::string(U("NcElementId"))); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointNmos.html - web::json::value make_nc_touchpoint_nmos_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResult.html + web::json::value make_nc_method_result_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Context NMOS resource")), nmos::fields::nc::resource, value::string(U("NcTouchpointResourceNmos")), false, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Touchpoint class for NMOS resources")), U("NcTouchpointNmos"), fields, value::string(U("NcTouchpoint"))); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Status for the invoked method")), nmos::fields::nc::status, value::string(U("NcMethodStatus")), false, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Base result of the invoked method")), U("NcMethodResult"), fields, value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointNmosChannelMapping.html - web::json::value make_nc_touchpoint_nmos_channel_mapping_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultBlockMemberDescriptors.html + web::json::value make_nc_method_result_block_member_descriptors_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("Context Channel Mapping resource")), nmos::fields::nc::resource, value::string(U("NcTouchpointResourceNmosChannelMapping")), false, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Touchpoint class for NMOS IS-08 resources")), U("NcTouchpointNmosChannelMapping"), fields, value::string(U("NcTouchpoint"))); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Block member descriptors method result value")), nmos::fields::nc::value, value::string(U("NcBlockMemberDescriptor")), false, true)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing block member descriptors as the value")), U("NcMethodResultBlockMemberDescriptors"), fields, value::string(U("NcMethodResult"))); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResource.html - web::json::value make_nc_touchpoint_resource_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultClassDescriptor.html + web::json::value make_nc_method_result_class_descriptor_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("The type of the resource")), nmos::fields::nc::resource_type, value::string(U("NcString")), false, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Touchpoint resource class")), U("NcTouchpointResource"), fields, value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Class descriptor method result value")), nmos::fields::nc::value, value::string(U("NcClassDescriptor")), false, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing a class descriptor as the value")), U("NcMethodResultClassDescriptor"), fields, value::string(U("NcMethodResult"))); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResourceNmos.html - web::json::value make_nc_touchpoint_resource_nmos_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultDatatypeDescriptor.html + web::json::value make_nc_method_result_datatype_descriptor_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("NMOS resource UUID")), nmos::fields::nc::id, value::string(U("NcUuid")), false, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Touchpoint resource class for NMOS resources")), U("NcTouchpointResourceNmos"), fields, value::string(U("NcTouchpointResource"))); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Datatype descriptor method result value")), nmos::fields::nc::value, value::string(U("NcDatatypeDescriptor")), false, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing a datatype descriptor as the value")), U("NcMethodResultDatatypeDescriptor"), fields, value::string(U("NcMethodResult"))); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResourceNmosChannelMapping.html - web::json::value make_nc_touchpoint_resource_nmos_channel_mapping_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultError.html + web::json::value make_nc_method_result_error_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, make_nc_field_descriptor(value::string(U("IS-08 Audio Channel Mapping input or output id")), nmos::fields::nc::io_id, value::string(U("NcString")), false, false)); - return make_nc_datatype_descriptor_struct(value::string(U("Touchpoint resource class for NMOS resources")), U("NcTouchpointResourceNmosChannelMapping"), fields, value::string(U("NcTouchpointResourceNmos"))); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Error message")), nmos::fields::nc::error_message, value::string(U("NcString")), false, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Error result - to be used when the method call encounters an error")), U("NcMethodResultError"), fields, value::string(U("NcMethodResult"))); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcUri.html - web::json::value make_nc_uri_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultId.html + web::json::value make_nc_method_result_id_datatype() + { + using web::json::value; - return make_nc_datatype_typedef(value::string(U("Uniform resource identifier")), U("NcUri"), false, U("NcString")); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Id result value")), nmos::fields::nc::value, value::string(U("NcId")), false, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Id method result")), U("NcMethodResultId"), fields, value::string(U("NcMethodResult"))); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcUuid.html - web::json::value make_nc_uuid_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultLength.html + web::json::value make_nc_method_result_length_datatype() + { + using web::json::value; - return make_nc_datatype_typedef(value::string(U("UUID")), U("NcUuid"), false, U("NcString")); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Length result value")), nmos::fields::nc::value, value::string(U("NcUint32")), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Length method result")), U("NcMethodResultLength"), fields, value::string(U("NcMethodResult"))); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcVersionCode.html - web::json::value make_nc_version_code_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultPropertyValue.html + web::json::value make_nc_method_result_property_value_datatype() + { + using web::json::value; - return make_nc_datatype_typedef(value::string(U("Version code in semantic versioning format")), U("NcVersionCode"), false, U("NcString")); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Getter method value for the associated property")), nmos::fields::nc::value, value::null(), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Result when invoking the getter method associated with a property")), U("NcMethodResultPropertyValue"), fields, value::string(U("NcMethodResult"))); + } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncconnectionstatus - web::json::value make_nc_connection_status_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodStatus.html + web::json::value make_nc_method_status_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful")), U("Ok"), 200)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful but targeted property is deprecated")), U("PropertyDeprecated"), 298)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful but method is deprecated")), U("MethodDeprecated"), 299)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Badly-formed command (e.g. the incoming command has invalid message encoding and cannot be parsed by the underlying protocol)")), U("BadCommandFormat"), 400)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Client is not authorized")), U("Unauthorized"), 401)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Command addresses a nonexistent object")), U("BadOid"), 404)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Attempt to change read-only state")), U("Readonly"), 405)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call is invalid in current operating context (e.g. attempting to invoke a method when the object is disabled)")), U("InvalidRequest"), 406)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("There is a conflict with the current state of the device")), U("Conflict"), 409)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Something was too big")), U("BufferOverflow"), 413)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Index is outside the available range")), U("IndexOutOfBounds"), 414)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method parameter does not meet expectations (e.g. attempting to invoke a method with an invalid type for one of its parameters)")), U("ParameterError"), 417)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed object is locked")), U("Locked"), 423)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Internal device error")), U("DeviceError"), 500)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed method is not implemented by the addressed object")), U("MethodNotImplemented"), 501)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed property is not implemented by the addressed object")), U("PropertyNotImplemented"), 502)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("The device is not ready to handle any commands")), U("NotReady"), 503)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call did not finish within the allotted time")), U("Timeout"), 504)); + return details::make_nc_datatype_descriptor_enum(value::string(U("Method invokation status")), U("NcMethodStatus"), items); + } - auto items = value::array(); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("This is the value when there is no receiver")), U("Undefined"), 0)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Connected to a stream")), U("Connected"), 1)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Not connected to a stream")), U("Disconnected"), 2)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("A connection error was encountered")), U("ConnectionError"), 3)); - return make_nc_datatype_descriptor_enum(value::string(U("Connection status enum data typee")), U("NcConnectionStatus"), items); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcName.html + web::json::value make_nc_name_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncpayloadstatus - web::json::value make_nc_payload_status_datatype() - { - using web::json::value; + return details::make_nc_datatype_typedef(value::string(U("Programmatically significant name, alphanumerics + underscore, no spaces")), U("NcName"), false, U("NcString")); + } - auto items = value::array(); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("This is the value when there's no connection")), U("Undefined"), 0)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Payload is being received without errors and is the correct type")), U("PayloadOK"), 1)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("Payload is being received but is of an unsupported type")), U("PayloadFormatUnsupported"), 2)); - web::json::push_back(items, make_nc_enum_item_descriptor(value::string(U("A payload error was encountered")), U("PayloadError"), 3)); - return make_nc_datatype_descriptor_enum(value::string(U("Connection status enum data typee")), U("NcPayloadStatus"), items); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcOid.html + web::json::value make_nc_oid_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject - web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) - { - using web::json::value; + return details::make_nc_datatype_typedef(value::string(U("Object id")), U("NcOid"), false, U("NcUint32")); + } - const auto id = utility::conversions::details::to_string_t(oid); -// auto data = make_resource_core(id, user_label.is_null() ? U("") : user_label.as_string(), {}); - value data; - data[nmos::fields::id] = value::string(id); // required for nmos::resource - data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); - data[nmos::fields::nc::oid] = oid; - data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); - data[nmos::fields::nc::owner] = owner; - data[nmos::fields::nc::role] = value::string(role); - data[nmos::fields::nc::user_label] = user_label; - data[nmos::fields::nc::touchpoints] = touchpoints; - data[nmos::fields::nc::runtime_property_constraints] = runtime_property_constraints; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcOrganizationId.html + web::json::value make_nc_organization_id_datatype() + { + using web::json::value; - return data; - } + return details::make_nc_datatype_typedef(value::string(U("Unique 24-bit organization id")), U("NcOrganizationId"), false, U("NcInt32")); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock - web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members) - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraints.html + web::json::value make_nc_parameter_constraints_datatype() + { + using web::json::value; - auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); - data[nmos::fields::nc::enabled] = value::boolean(enabled); - data[nmos::fields::nc::members] = members; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Default value")), nmos::fields::nc::default_value, value::null(), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Abstract parameter constraints class")), U("NcParameterConstraints"), fields, value::null()); + } - return data; - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraintsNumber.html + web::json::value make_nc_parameter_constraints_number_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker - web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled) - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional maximum")), nmos::fields::nc::maximum, value::null(), true, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional minimum")), nmos::fields::nc::minimum, value::null(), true, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional step")), nmos::fields::nc::step, value::null(), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Number parameter constraints class")), U("NcParameterConstraintsNumber"), fields, value::string(U("NcParameterConstraints"))); + } - auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); - data[nmos::fields::nc::enabled] = value::boolean(enabled); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraintsString.html + web::json::value make_nc_parameter_constraints_string_datatype() + { + using web::json::value; - return data; - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Maximum characters allowed")), nmos::fields::nc::max_characters, value::string(U("NcUint32")), true, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Regex pattern")), nmos::fields::nc::pattern, value::string(U("NcRegex")), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("String parameter constraints class")), U("NcParameterConstraintsString"), fields, value::string(U("NcParameterConstraints"))); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager - web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) - { - return make_nc_object(class_id, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterDescriptor.html + web::json::value make_nc_parameter_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of parameter")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of parameter's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a method parameter")), U("NcParameterDescriptor"), fields, value::string(U("NcDescriptor"))); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager - web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, - const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, - const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause) - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcProduct.html + web::json::value make_nc_product_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Product name")), nmos::fields::nc::name, value::string(U("NcString")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's unique key to product - model number, SKU, etc")), nmos::fields::nc::key, value::string(U("NcString")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's product revision level code")), nmos::fields::nc::revision_level, value::string(U("NcString")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Brand name under which product is sold")), nmos::fields::nc::brand_name, value::string(U("NcString")), true, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Unique UUID of product (not product instance)")), nmos::fields::nc::uuid, value::string(U("NcUuid")), true, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Text description of product")), nmos::fields::nc::description, value::string(U("NcString")), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Product descriptor")), U("NcProduct"), fields, value::null()); + } - auto data = details::make_nc_manager(nc_device_manager_class_id, oid, true, owner, U("DeviceManager"), user_label, touchpoints, runtime_property_constraints); - data[nmos::fields::nc::nc_version] = value::string(U("v1.0.0")); - data[nmos::fields::nc::manufacturer] = manufacturer; - data[nmos::fields::nc::product] = product; - data[nmos::fields::nc::serial_number] = value::string(serial_number); - data[nmos::fields::nc::user_inventory_code] = user_inventory_code; - data[nmos::fields::nc::device_name] = device_name; - data[nmos::fields::nc::device_role] = device_role; - data[nmos::fields::nc::operational_state] = operational_state; - data[nmos::fields::nc::reset_cause] = reset_cause; - data[nmos::fields::nc::message] = value::null(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyChangeType.html + web::json::value make_nc_property_change_type_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Current value changed")), U("ValueChanged"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item added")), U("SequenceItemAdded"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item changed")), U("SequenceItemChanged"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item removed")), U("SequenceItemRemoved"), 3)); + return details::make_nc_datatype_descriptor_enum(value::string(U("Type of property change")), U("NcPropertyChangeType"), items); + } - return data; - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyChangedEventData.html + web::json::value make_nc_property_changed_event_data_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("The id of the property that changed")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Information regarding the change type")), nmos::fields::nc::change_type, value::string(U("NcPropertyChangeType")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property-type specific value")), nmos::fields::nc::value, value::null(), true, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Index of sequence item if the property is a sequence")), nmos::fields::nc::sequence_item_index, value::string(U("NcId")), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Payload of property-changed event")), U("NcPropertyChangedEventData"), fields, value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state) - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraints.html + web::json::value make_nc_property_contraints_datatype() + { + using web::json::value; - auto data = make_nc_manager(nc_class_manager_class_id, oid, true, owner, U("ClassManager"), user_label, touchpoints, runtime_property_constraints); + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("The id of the property being constrained")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional default value")), nmos::fields::nc::default_value, value::null(), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Property constraints class")), U("NcPropertyConstraints"), fields, value::null()); + } - // core control classes - data[nmos::fields::nc::control_classes] = value::array(); - auto& control_classes = data[nmos::fields::nc::control_classes]; - for (const auto& control_class : control_protocol_state.control_classes) - { - auto& ctl_class = control_class.second; - web::json::push_back(control_classes, make_nc_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.fixed_role, ctl_class.properties, ctl_class.methods, ctl_class.events)); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraintsNumber.html + web::json::value make_nc_property_constraints_number_datatype() + { + using web::json::value; - // core datatypes - data[nmos::fields::nc::datatypes] = value::array(); - auto& datatypes = data[nmos::fields::nc::datatypes]; - for (const auto& datatype : control_protocol_state.datatypes) - { - web::json::push_back(datatypes, datatype.second.descriptor); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional maximum")), nmos::fields::nc::maximum, value::null(), true, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional minimum")), nmos::fields::nc::minimum, value::null(), true, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional step")), nmos::fields::nc::step, value::null(), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Number property constraints class")), U("NcPropertyConstraintsNumber"), fields, value::string(U("NcPropertyConstraints"))); + } - return data; - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraintsString.html + web::json::value make_nc_property_constraints_string_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Maximum characters allowed")), nmos::fields::nc::max_characters, value::string(U("NcUint32")), true, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Regex pattern")), nmos::fields::nc::pattern, value::string(U("NcRegex")), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("String property constraints class")), U("NcPropertyConstraintsString"), fields, value::string(U("NcPropertyConstraints"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyDescriptor.html + web::json::value make_nc_property_descriptor_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property id with level and index")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of property")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of property's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is read-only")), nmos::fields::nc::is_read_only, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false)); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class property")), U("NcPropertyDescriptor"), fields, value::string(U("NcDescriptor"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyId.html + web::json::value make_nc_property_id_datatype() + { + using web::json::value; + + return details::make_nc_datatype_descriptor_struct(value::string(U("Property id which contains the level and index")), U("NcPropertyId"), value::array(), value::string(U("NcElementId"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcRegex.html + web::json::value make_nc_regex_datatype() + { + using web::json::value; + + return details::make_nc_datatype_typedef(value::string(U("Regex pattern")), U("NcRegex"), false, U("NcString")); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcResetCause.html + web::json::value make_nc_reset_cause_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Power on")), U("PowerOn"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Internal error")), U("InternalError"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Upgrade")), U("Upgrade"), 3)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Controller request")), U("ControllerRequest"), 4)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Manual request from the front panel")), U("ManualReset"), 5)); + return details::make_nc_datatype_descriptor_enum(value::string(U("Reset cause enum")), U("NcResetCause"), items); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcRolePath.html + web::json::value make_nc_role_path_datatype() + { + using web::json::value; + + return details::make_nc_datatype_typedef(value::string(U("Role path")), U("NcRolePath"), true, U("NcString")); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTimeInterval.html + web::json::value make_nc_time_interval_datatype() + { + using web::json::value; + + return details::make_nc_datatype_typedef(value::string(U("Time interval described in nanoseconds")), U("NcTimeInterval"), false, U("NcInt64")); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpoint.html + web::json::value make_nc_touchpoint_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Context namespace")), nmos::fields::nc::context_namespace, value::string(U("NcString")), false, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Base touchpoint class")), U("NcTouchpoint"), fields, value::null()); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointNmos.html + web::json::value make_nc_touchpoint_nmos_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Context NMOS resource")), nmos::fields::nc::resource, value::string(U("NcTouchpointResourceNmos")), false, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Touchpoint class for NMOS resources")), U("NcTouchpointNmos"), fields, value::string(U("NcTouchpoint"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointNmosChannelMapping.html + web::json::value make_nc_touchpoint_nmos_channel_mapping_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Context Channel Mapping resource")), nmos::fields::nc::resource, value::string(U("NcTouchpointResourceNmosChannelMapping")), false, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Touchpoint class for NMOS IS-08 resources")), U("NcTouchpointNmosChannelMapping"), fields, value::string(U("NcTouchpoint"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResource.html + web::json::value make_nc_touchpoint_resource_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("The type of the resource")), nmos::fields::nc::resource_type, value::string(U("NcString")), false, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Touchpoint resource class")), U("NcTouchpointResource"), fields, value::null()); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResourceNmos.html + web::json::value make_nc_touchpoint_resource_nmos_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("NMOS resource UUID")), nmos::fields::nc::id, value::string(U("NcUuid")), false, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Touchpoint resource class for NMOS resources")), U("NcTouchpointResourceNmos"), fields, value::string(U("NcTouchpointResource"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResourceNmosChannelMapping.html + web::json::value make_nc_touchpoint_resource_nmos_channel_mapping_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("IS-08 Audio Channel Mapping input or output id")), nmos::fields::nc::io_id, value::string(U("NcString")), false, false)); + return details::make_nc_datatype_descriptor_struct(value::string(U("Touchpoint resource class for NMOS resources")), U("NcTouchpointResourceNmosChannelMapping"), fields, value::string(U("NcTouchpointResourceNmos"))); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcUri.html + web::json::value make_nc_uri_datatype() + { + using web::json::value; + + return details::make_nc_datatype_typedef(value::string(U("Uniform resource identifier")), U("NcUri"), false, U("NcString")); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcUuid.html + web::json::value make_nc_uuid_datatype() + { + using web::json::value; + + return details::make_nc_datatype_typedef(value::string(U("UUID")), U("NcUuid"), false, U("NcString")); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcVersionCode.html + web::json::value make_nc_version_code_datatype() + { + using web::json::value; + + return details::make_nc_datatype_typedef(value::string(U("Version code in semantic versioning format")), U("NcVersionCode"), false, U("NcString")); + } + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncconnectionstatus + web::json::value make_nc_connection_status_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("This is the value when there is no receiver")), U("Undefined"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Connected to a stream")), U("Connected"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Not connected to a stream")), U("Disconnected"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("A connection error was encountered")), U("ConnectionError"), 3)); + return details::make_nc_datatype_descriptor_enum(value::string(U("Connection status enum data typee")), U("NcConnectionStatus"), items); + } + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncpayloadstatus + web::json::value make_nc_payload_status_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("This is the value when there's no connection")), U("Undefined"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Payload is being received without errors and is the correct type")), U("PayloadOK"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Payload is being received but is of an unsupported type")), U("PayloadFormatUnsupported"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("A payload error was encountered")), U("PayloadError"), 3)); + return details::make_nc_datatype_descriptor_enum(value::string(U("Connection status enum data typee")), U("NcPayloadStatus"), items); } } diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 387bb6978..8d2c4208b 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -2,7 +2,7 @@ #define NMOS_CONTROL_PROTOCOL_RESOURCE_H #include "cpprest/json_utils.h" -#include "nmos/control_protocol_class_id.h" +#include "nmos/control_protocol_typedefs.h" namespace web { @@ -21,184 +21,25 @@ namespace nmos namespace details { - namespace nc_message_type - { - enum type - { - command = 0, - command_response = 1, - notification = 2, - subscription = 3, - subscription_response = 4, - error = 5 - }; - } - - // Method invokation status - namespace nc_method_status - { - enum status - { - ok = 200, // Method call was successful - property_deprecated = 298, // Method call was successful but targeted property is deprecated - method_deprecated = 299, // Method call was successful but method is deprecated - bad_command_format = 400, // Badly-formed command - unathorized = 401, // Client is not authorized - bad_oid = 404, // Command addresses a nonexistent object - read_only = 405, // Attempt to change read-only state - invalid_request = 406, // Method call is invalid in current operating context - conflict = 409, // There is a conflict with the current state of the device - buffer_overflow = 413, // Something was too big - index_out_of_bounds = 414, // Index is outside the available range - parameter_error = 417, // Method parameter does not meet expectations - locked = 423, // Addressed object is locked - device_error = 500, // Internal device error - method_not_implemented = 501, // Addressed method is not implemented by the addressed object - property_not_implemented = 502, // Addressed property is not implemented by the addressed object - not_ready = 503, // The device is not ready to handle any commands - timeout = 504, // Method call did not finish within the allotted time - property_version_error = 505 // Incompatible protocol version - }; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodresult - struct nc_method_result - { - nc_method_status::status status; - }; - - // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-response-message-type - web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message); - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result); - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value); - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, uint32_t value); - - // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-message-type - web::json::value make_control_protocol_message_response(nc_message_type::type type, const web::json::value& responses); - // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#error-messages - web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message); - - // Datatype type - namespace nc_datatype_type - { - enum type - { - Primitive = 0, - Typedef = 1, - Struct = 2, - Enum = 3 - }; - } - - // Device generic operational state - namespace nc_device_generic_state - { - enum state - { - unknown = 0, // Unknown - normal_operation = 1, // Normal operation - initializing = 2, // Device is initializing - updating = 3, // Device is performing a software or firmware update - licensing_error = 4, // Device is experiencing a licensing error - internal_error = 5 // Device is experiencing an internal error - }; - } - - // Reset cause enum - namespace nc_reset_cause - { - enum cause - { - unknown = 0, // Unknown - power_on = 1, // Power on - internal_error = 2, // Internal error - upgrade = 3, // Upgrade - controller_request = 4, // Controller request - manual_reset = 5 // Manual request from the front panel - }; - } - - // NcConnectionStatus - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncconnectionstatus - namespace nc_connection_status - { - enum status - { - undefined = 0, // This is the value when there is no receiver - connected = 1, // Connected to a stream - disconnected = 2, // Not connected to a stream - connection_error = 3 // A connection error was encountered - }; - } - - // NcPayloadStatus - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncpayloadstatus - namespace nc_payload_status - { - enum status - { - undefined = 0, // This is the value when there's no connection. - payload_ok = 1, // Payload is being received without errors and is the correct type - payload_format_unsupported = 2, // Payload is being received but is of an unsupported type - payloadError = 3 // A payload error was encountered - }; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncid - typedef uint32_t nc_id; - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncoid - typedef uint32_t nc_oid; - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncuri - typedef utility::string_t nc_uri; - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncuuid - typedef utility::string_t nc_uuid; - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid - const nc_class_id nc_object_class_id({ 1 }); - const nc_class_id nc_block_class_id({ 1, 1 }); - const nc_class_id nc_worker_class_id({ 1, 2 }); - const nc_class_id nc_manager_class_id({ 1, 3 }); - const nc_class_id nc_device_manager_class_id({ 1, 3, 1 }); - const nc_class_id nc_class_manager_class_id({ 1, 3, 2 }); - const nc_class_id nc_ident_beacon_class_id({ 1, 2, 2 }); - const nc_class_id nc_receiver_monitor_class_id({ 1, 2, 3 }); - const nc_class_id nc_receiver_monitor_protected_class_id({ 1, 2, 3, 1 }); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nctouchpoint - typedef utility::string_t nc_touch_point; - - web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message); - - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result); - - // value can be - // sequence - // NcClassDescriptor - // NcDatatypeDescriptor - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value); - - // message response - // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-message-type - web::json::value make_control_protocol_message_response(nc_message_type::type type, const web::json::value& responses); - - // error message - // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#error-messages - web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid - web::json::value make_nc_element_id(uint16_t level, uint16_t index); + //web::json::value make_nc_element_id(uint16_t level, uint16_t index); + web::json::value make_nc_element_id(const nc_element_id& element_id); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventid - web::json::value make_nc_event_id(uint16_t level, uint16_t index); + //web::json::value make_nc_event_id(uint16_t level, uint16_t index); + web::json::value make_nc_event_id(const nc_event_id& event_id); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodid - web::json::value make_nc_method_id(uint16_t level, uint16_t index); + //web::json::value make_nc_method_id(uint16_t level, uint16_t index); + web::json::value make_nc_method_id(const nc_method_id& event_id); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertyid - web::json::value make_nc_property_id(uint16_t level, uint16_t index); + //web::json::value make_nc_property_id(uint16_t level, uint16_t index); + web::json::value make_nc_property_id(const nc_property_id& event_id); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid + web::json::value make_nc_class_id(const nc_class_id& class_id); + nc_class_id parse_nc_class_id(const web::json::array& class_id); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanufacturer web::json::value make_nc_manufacturer(const utility::string_t& name, const web::json::value& organization_id = web::json::value::null(), const web::json::value& website = web::json::value::null()); @@ -221,45 +62,55 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblockmemberdescriptor // description can be null // user_label can be null - web::json::value make_nc_block_member_descriptor(const web::json::value& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const web::json::value& class_id, const web::json::value& user_label, nc_oid owner); + web::json::value make_nc_block_member_descriptor(const web::json::value& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const web::json::value& user_label, nc_oid owner); + web::json::value make_nc_block_member_descriptor(const utility::string_t& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const utility::string_t& user_label, nc_oid owner); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassdescriptor // description can be null // fixedRole can be null web::json::value make_nc_class_descriptor(const web::json::value& description, const nc_class_id& class_id, const utility::string_t& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); + web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncenumitemdescriptor // description can be null web::json::value make_nc_enum_item_descriptor(const web::json::value& description, const utility::string_t& name, uint16_t val); + web::json::value make_nc_enum_item_descriptor(const utility::string_t& description, const utility::string_t& name, uint16_t val); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventdescriptor // description can be null // id = make_nc_event_id(level, index) - web::json::value make_nc_event_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const utility::string_t& event_datatype, bool is_deprecated); + web::json::value make_nc_event_descriptor(const web::json::value& description, const nc_event_id& id, const utility::string_t& name, const utility::string_t& event_datatype, bool is_deprecated); + web::json::value make_nc_event_descriptor(const utility::string_t& description, const nc_event_id& id, const utility::string_t& name, const utility::string_t& event_datatype, bool is_deprecated); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncfielddescriptor // description can be null // type_name can be null // constraints can be null web::json::value make_nc_field_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_field_descriptor(const utility::string_t& description, const utility::string_t& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethoddescriptor // description can be null // id = make_nc_method_id(level, index) // sequence parameters - web::json::value make_nc_method_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated); + web::json::value make_nc_method_descriptor(const web::json::value& description, const nc_method_id& id, const utility::string_t& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated); + web::json::value make_nc_method_descriptor(const utility::string_t& description, const nc_method_id& id, const utility::string_t& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncparameterdescriptor // description can be null // type_name can be null web::json::value make_nc_parameter_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const utility::string_t& name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const utility::string_t& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertydescriptor // description can be null // id = make_nc_property_id(level, index); // type_name can be null // constraints can be null - web::json::value make_nc_property_descriptor(const web::json::value& description, const web::json::value& id, const utility::string_t& name, const web::json::value& type_name, + web::json::value make_nc_property_descriptor(const web::json::value& description, const nc_property_id& id, const utility::string_t& name, const web::json::value& type_name, + bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_property_descriptor(const utility::string_t& description, const nc_property_id& id, const utility::string_t& name, const utility::string_t& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptor @@ -290,188 +141,6 @@ namespace nmos // constraints can be null web::json::value make_nc_datatype_typedef(const web::json::value& description, const utility::string_t& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints = web::json::value::null()); - // Control class models - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/#control-class-models-for-branch-v10-dev - // - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.html - web::json::value make_nc_object_class(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.1.html - web::json::value make_nc_block_class(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.2.html - web::json::value make_nc_worker_class(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.html - web::json::value make_nc_manager_class(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.1.html - web::json::value make_nc_device_manager_class(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.2.html - web::json::value make_nc_class_manager_class(); - - // control classes proprties/methods/events - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject - web::json::value make_nc_object_properties(); - web::json::value make_nc_object_methods(); - web::json::value make_nc_object_events(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock - web::json::value make_nc_block_properties(); - web::json::value make_nc_block_methods(); - web::json::value make_nc_block_events(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker - web::json::value make_nc_worker_properties(); - web::json::value make_nc_worker_methods(); - web::json::value make_nc_worker_events(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager - web::json::value make_nc_manager_properties(); - web::json::value make_nc_manager_methods(); - web::json::value make_nc_manager_events(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager - web::json::value make_nc_device_manager_properties(); - web::json::value make_nc_device_manager_methods(); - web::json::value make_nc_device_manager_events(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager_properties(); - web::json::value make_nc_class_manager_methods(); - web::json::value make_nc_class_manager_events(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor - web::json::value make_nc_receiver_monitor_properties(); - web::json::value make_nc_receiver_monitor_methods(); - web::json::value make_nc_receiver_monitor_events(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitorprotected - web::json::value make_nc_receiver_monitor_protected_properties(); - web::json::value make_nc_receiver_monitor_protected_methods(); - web::json::value make_nc_receiver_monitor_protected_events(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon - web::json::value make_nc_ident_beacon_properties(); - web::json::value make_nc_ident_beacon_methods(); - web::json::value make_nc_ident_beacon_events(); - - // Datatype models - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/#datatype-models-for-branch-v10-dev - // - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcBlockMemberDescriptor.html - web::json::value make_nc_block_member_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcClassDescriptor.html - web::json::value make_nc_class_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcClassId.html - web::json::value make_nc_class_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptor.html - web::json::value make_nc_datatype_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorEnum.html - web::json::value make_nc_datatype_descriptor_enum_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorPrimitive.html - web::json::value make_nc_datatype_descriptor_primitive_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorStruct.html - web::json::value make_nc_datatype_descriptor_struct_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorTypeDef.html - web::json::value make_nc_datatype_descriptor_type_def_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeType.html - web::json::value make_nc_datatype_type_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDescriptor.html - web::json::value make_nc_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDeviceGenericState.html - web::json::value make_nc_device_generic_state_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDeviceOperationalState.html - web::json::value make_nc_device_operational_state_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcElementId.html - web::json::value make_nc_element_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEnumItemDescriptor.html - web::json::value make_nc_enum_item_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEventDescriptor.html - web::json::value make_nc_event_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEventId.html - web::json::value make_nc_event_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcFieldDescriptor.html - web::json::value make_nc_field_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcId.html - web::json::value make_nc_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcManufacturer.html - web::json::value make_nc_manufacturer_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodDescriptor.html - web::json::value make_nc_method_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodId.html - web::json::value make_nc_method_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResult.html - web::json::value make_nc_method_result_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultBlockMemberDescriptors.html - web::json::value make_nc_method_result_block_member_descriptors_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultClassDescriptor.html - web::json::value make_nc_method_result_class_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultDatatypeDescriptor.html - web::json::value make_nc_method_result_datatype_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultError.html - web::json::value make_nc_method_result_error_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultId.html - web::json::value make_nc_method_result_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultLength.html - web::json::value make_nc_method_result_length_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultPropertyValue.html - web::json::value make_nc_method_result_property_value_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodStatus.html - web::json::value make_nc_method_status_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcName.html - web::json::value make_nc_name_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcOid.html - web::json::value make_nc_oid_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcOrganizationId.html - web::json::value make_nc_organization_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraints.html - web::json::value make_nc_parameter_constraints_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraintsNumber.html - web::json::value make_nc_parameter_constraints_number_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraintsString.html - web::json::value make_nc_parameter_constraints_string_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterDescriptor.html - web::json::value make_nc_parameter_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcProduct.html - web::json::value make_nc_product_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyChangeType.html - web::json::value make_nc_property_change_type_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyChangedEventData.html - web::json::value make_nc_property_changed_event_data_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraints.html - web::json::value make_nc_property_contraints_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraintsNumber.html - web::json::value make_nc_property_constraints_number_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraintsString.html - web::json::value make_nc_property_constraints_string_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyDescriptor.html - web::json::value make_nc_property_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyId.html - web::json::value make_nc_property_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcRegex.html - web::json::value make_nc_regex_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcResetCause.html - web::json::value make_nc_reset_cause_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcRolePath.html - web::json::value make_nc_role_path_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTimeInterval.html - web::json::value make_nc_time_interval_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpoint.html - web::json::value make_nc_touchpoint_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointNmos.html - web::json::value make_nc_touchpoint_nmos_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointNmosChannelMapping.html - web::json::value make_nc_touchpoint_nmos_channel_mapping_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResource.html - web::json::value make_nc_touchpoint_resource_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResourceNmos.html - web::json::value make_nc_touchpoint_resource_nmos_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResourceNmosChannelMapping.html - web::json::value make_nc_touchpoint_resource_nmos_channel_mapping_datatype(); - // See // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcUri.html - web::json::value make_nc_uri_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcUuid.html - web::json::value make_nc_uuid_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcVersionCode.html - web::json::value make_nc_version_code_datatype(); - - // Monitoring datatypes - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#datatypes - // - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncconnectionstatus - web::json::value make_nc_connection_status_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncpayloadstatus - web::json::value make_nc_payload_status_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); @@ -490,8 +159,210 @@ namespace nmos const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager(details::nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state); + web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state); } + + // message response + // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-message-type + web::json::value make_control_protocol_message_response(nc_message_type::type type, const web::json::value& responses); + + // error message + // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#error-messages + web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message); + + // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-response-message-type + web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message); + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result); + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value); // value can be sequence, NcClassDescriptor, NcDatatypeDescriptor + web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, uint32_t value); + + // Control class models + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/#control-class-models-for-branch-v10-dev + // + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.html + web::json::value make_nc_object_class(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.1.html + web::json::value make_nc_block_class(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.2.html + web::json::value make_nc_worker_class(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.html + web::json::value make_nc_manager_class(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.1.html + web::json::value make_nc_device_manager_class(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.2.html + web::json::value make_nc_class_manager_class(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon + web::json::value make_nc_ident_beacon_class(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor + web::json::value make_nc_receiver_monitor_class(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitorprotected + web::json::value make_nc_receiver_monitor_protected_class(); + + // control classes proprties/methods/events + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject + web::json::value make_nc_object_properties(); + web::json::value make_nc_object_methods(); + web::json::value make_nc_object_events(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + web::json::value make_nc_block_properties(); + web::json::value make_nc_block_methods(); + web::json::value make_nc_block_events(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker + web::json::value make_nc_worker_properties(); + web::json::value make_nc_worker_methods(); + web::json::value make_nc_worker_events(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager + web::json::value make_nc_manager_properties(); + web::json::value make_nc_manager_methods(); + web::json::value make_nc_manager_events(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + web::json::value make_nc_device_manager_properties(); + web::json::value make_nc_device_manager_methods(); + web::json::value make_nc_device_manager_events(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + web::json::value make_nc_class_manager_properties(); + web::json::value make_nc_class_manager_methods(); + web::json::value make_nc_class_manager_events(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor + web::json::value make_nc_receiver_monitor_properties(); + web::json::value make_nc_receiver_monitor_methods(); + web::json::value make_nc_receiver_monitor_events(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitorprotected + web::json::value make_nc_receiver_monitor_protected_properties(); + web::json::value make_nc_receiver_monitor_protected_methods(); + web::json::value make_nc_receiver_monitor_protected_events(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon + web::json::value make_nc_ident_beacon_properties(); + web::json::value make_nc_ident_beacon_methods(); + web::json::value make_nc_ident_beacon_events(); + + // Datatype models + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/#datatype-models-for-branch-v10-dev + // + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcBlockMemberDescriptor.html + web::json::value make_nc_block_member_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcClassDescriptor.html + web::json::value make_nc_class_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcClassId.html + web::json::value make_nc_class_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptor.html + web::json::value make_nc_datatype_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorEnum.html + web::json::value make_nc_datatype_descriptor_enum_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorPrimitive.html + web::json::value make_nc_datatype_descriptor_primitive_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorStruct.html + web::json::value make_nc_datatype_descriptor_struct_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorTypeDef.html + web::json::value make_nc_datatype_descriptor_type_def_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeType.html + web::json::value make_nc_datatype_type_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDescriptor.html + web::json::value make_nc_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDeviceGenericState.html + web::json::value make_nc_device_generic_state_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDeviceOperationalState.html + web::json::value make_nc_device_operational_state_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcElementId.html + web::json::value make_nc_element_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEnumItemDescriptor.html + web::json::value make_nc_enum_item_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEventDescriptor.html + web::json::value make_nc_event_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEventId.html + web::json::value make_nc_event_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcFieldDescriptor.html + web::json::value make_nc_field_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcId.html + web::json::value make_nc_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcManufacturer.html + web::json::value make_nc_manufacturer_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodDescriptor.html + web::json::value make_nc_method_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodId.html + web::json::value make_nc_method_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResult.html + web::json::value make_nc_method_result_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultBlockMemberDescriptors.html + web::json::value make_nc_method_result_block_member_descriptors_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultClassDescriptor.html + web::json::value make_nc_method_result_class_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultDatatypeDescriptor.html + web::json::value make_nc_method_result_datatype_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultError.html + web::json::value make_nc_method_result_error_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultId.html + web::json::value make_nc_method_result_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultLength.html + web::json::value make_nc_method_result_length_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultPropertyValue.html + web::json::value make_nc_method_result_property_value_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodStatus.html + web::json::value make_nc_method_status_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcName.html + web::json::value make_nc_name_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcOid.html + web::json::value make_nc_oid_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcOrganizationId.html + web::json::value make_nc_organization_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraints.html + web::json::value make_nc_parameter_constraints_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraintsNumber.html + web::json::value make_nc_parameter_constraints_number_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraintsString.html + web::json::value make_nc_parameter_constraints_string_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterDescriptor.html + web::json::value make_nc_parameter_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcProduct.html + web::json::value make_nc_product_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyChangeType.html + web::json::value make_nc_property_change_type_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyChangedEventData.html + web::json::value make_nc_property_changed_event_data_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraints.html + web::json::value make_nc_property_contraints_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraintsNumber.html + web::json::value make_nc_property_constraints_number_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraintsString.html + web::json::value make_nc_property_constraints_string_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyDescriptor.html + web::json::value make_nc_property_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyId.html + web::json::value make_nc_property_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcRegex.html + web::json::value make_nc_regex_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcResetCause.html + web::json::value make_nc_reset_cause_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcRolePath.html + web::json::value make_nc_role_path_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTimeInterval.html + web::json::value make_nc_time_interval_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpoint.html + web::json::value make_nc_touchpoint_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointNmos.html + web::json::value make_nc_touchpoint_nmos_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointNmosChannelMapping.html + web::json::value make_nc_touchpoint_nmos_channel_mapping_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResource.html + web::json::value make_nc_touchpoint_resource_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResourceNmos.html + web::json::value make_nc_touchpoint_resource_nmos_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResourceNmosChannelMapping.html + web::json::value make_nc_touchpoint_resource_nmos_channel_mapping_datatype(); + // See // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcUri.html + web::json::value make_nc_uri_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcUuid.html + web::json::value make_nc_uuid_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcVersionCode.html + web::json::value make_nc_version_code_datatype(); + + // Monitoring datatypes + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#datatypes + // + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncconnectionstatus + web::json::value make_nc_connection_status_datatype(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncpayloadstatus + web::json::value make_nc_payload_status_datatype(); } #endif diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index aa126ce1f..e3a5c5f5a 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -7,82 +7,76 @@ namespace nmos { + namespace details + { + // create block resource + nmos::resource make_block(nmos::nc_oid oid, const web::json::value& owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) + { + using web::json::value; + + auto data = details::make_nc_block(nc_block_class_id, oid, true, owner, role, value::string(user_label), touchpoints, runtime_property_constraints, true, members); + + return{ is12_versions::v1_0, types::nc_block, std::move(data), true }; + } + } + + // create block resource + nmos::resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) + { + using web::json::value; + + return details::make_block(oid, value(owner), role, user_label, touchpoints, runtime_property_constraints, members); + } + + // create Root block resource + nmos::resource make_root_block() + { + using web::json::value; + + return details::make_block(1, value::null(), U("root"), U("Root"), value::null(), value::null(), value::array()); + } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager - nmos::resource make_device_manager(details::nc_oid oid, nmos::resource& root_block, const nmos::settings& settings) + nmos::resource make_device_manager(nc_oid oid, const nmos::settings& settings) { using web::json::value; - auto& root_block_data = root_block.data; - const auto& owner = nmos::fields::nc::oid(root_block_data); const auto user_label = value::string(U("Device manager")); - const auto description = value::string(U("The device manager offers information about the product this device is representing")); const auto& manufacturer = details::make_nc_manufacturer(nmos::experimental::fields::manufacturer_name(settings)); const auto& product = details::make_nc_product(nmos::experimental::fields::product_name(settings), nmos::experimental::fields::product_key(settings), nmos::experimental::fields::product_key(settings)); const auto& serial_number = nmos::experimental::fields::serial_number(settings); const auto device_name = value::null(); const auto device_role = value::null(); - const auto& operational_state = details::make_nc_device_operational_state(details::nc_device_generic_state::normal_operation, value::null()); - - auto data = details::make_nc_device_manager(oid, owner, user_label, value::null(), value::null(), - manufacturer, product, serial_number, value::null(), device_name, device_role, operational_state, details::nc_reset_cause::unknown); + const auto& operational_state = details::make_nc_device_operational_state(nc_device_generic_state::normal_operation, value::null()); - // add NcDeviceManager block_member_descriptor to root block members - web::json::push_back(root_block_data[nmos::fields::nc::members], - details::make_nc_block_member_descriptor(description, nmos::fields::nc::role(data), oid, nmos::fields::nc::constant_oid(data), data.at(nmos::fields::nc::class_id), user_label, owner)); + auto data = details::make_nc_device_manager(oid, root_block_oid, user_label, value::null(), value::null(), + manufacturer, product, serial_number, value::null(), device_name, device_role, operational_state, nc_reset_cause::unknown); return{ is12_versions::v1_0, types::nc_device_manager, std::move(data), true }; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - nmos::resource make_class_manager(details::nc_oid oid, nmos::resource& root_block, const nmos::experimental::control_protocol_state& control_protocol_state) + nmos::resource make_class_manager(nc_oid oid, const nmos::experimental::control_protocol_state& control_protocol_state) { using web::json::value; - auto& root_block_data = root_block.data; - const auto& owner = nmos::fields::nc::oid(root_block_data); const auto user_label = value::string(U("Class manager")); - const auto description = value::string(U("The class manager offers access to control class and data type descriptors")); - auto data = details::make_nc_class_manager(oid, owner, user_label, value::null(), value::null(), control_protocol_state); - - // add NcClassManager block_member_descriptor to root block members - web::json::push_back(root_block_data[nmos::fields::nc::members], - details::make_nc_block_member_descriptor(description, nmos::fields::nc::role(data), oid, nmos::fields::nc::constant_oid(data), data.at(nmos::fields::nc::class_id), user_label, owner)); + auto data = details::make_nc_class_manager(oid, root_block_oid, user_label, value::null(), value::null(), control_protocol_state); return{ is12_versions::v1_0, types::nc_class_manager, std::move(data), true }; } - // create block resource - nmos::resource make_block(details::nc_oid oid, const web::json::value& owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) - { - using web::json::value; - - auto data = details::make_nc_block(details::nc_block_class_id, oid, true, owner, role, value::string(user_label), touchpoints, runtime_property_constraints, true, members); - - return{ is12_versions::v1_0, types::nc_block, std::move(data), true }; - } - nmos::resource make_block(details::nc_oid oid, details::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) - { - using web::json::value; - - return make_block(oid, value(owner), role, user_label, touchpoints, runtime_property_constraints, members); - } - - // create Root block resource - nmos::resource make_root_block() + // add to owner block member + bool add_member(const utility::string_t& child_description, const nmos::resource& child_block, nmos::resource& parent_block) { using web::json::value; - return make_block(1, value::null(), U("root"), U("Root")); - } - - // add member to nc_block - bool add_member_to_block(const utility::string_t& description, const web::json::value& nc_block, web::json::value& parent) - { - using web::json::value; + auto& parent = parent_block.data; + const auto& child = child_block.data; web::json::push_back(parent[nmos::fields::nc::members], - details::make_nc_block_member_descriptor(value::string(description), nmos::fields::nc::role(nc_block), nmos::fields::nc::oid(nc_block), nmos::fields::nc::constant_oid(nc_block), nc_block.at(nmos::fields::nc::class_id), nc_block.at(nmos::fields::nc::user_label), nmos::fields::nc::oid(parent))); + details::make_nc_block_member_descriptor(child_description, nmos::fields::nc::role(child), nmos::fields::nc::oid(child), nmos::fields::nc::constant_oid(child), details::parse_nc_class_id(nmos::fields::nc::class_id(child)), nmos::fields::nc::user_label(child), nmos::fields::nc::oid(parent))); return true; } diff --git a/Development/nmos/control_protocol_resources.h b/Development/nmos/control_protocol_resources.h index 205e16606..d3fdb44b1 100644 --- a/Development/nmos/control_protocol_resources.h +++ b/Development/nmos/control_protocol_resources.h @@ -1,8 +1,7 @@ #ifndef NMOS_CONTROL_PROTOCOL_RESOURCES_H #define NMOS_CONTROL_PROTOCOL_RESOURCES_H -#include -#include "nmos/control_protocol_resource.h" // for details::nc_oid definition +#include "nmos/control_protocol_typedefs.h" // for details::nc_oid definition #include "nmos/settings.h" namespace nmos @@ -14,21 +13,20 @@ namespace nmos struct resource; - // create device manager resource - nmos::resource make_device_manager(details::nc_oid oid, nmos::resource& root_block, const nmos::settings& settings); - - // create class manager resource - nmos::resource make_class_manager(details::nc_oid oid, nmos::resource& root_block, const nmos::experimental::control_protocol_state& control_protocol_state); - // create block resource - nmos::resource make_block(details::nc_oid oid, const web::json::value& owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), const web::json::value& members = web::json::value::array()); - nmos::resource make_block(details::nc_oid oid, details::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), const web::json::value& members = web::json::value::array()); - - // help function to add member to block - bool add_member_to_block(const utility::string_t& description, const web::json::value& nc_block, web::json::value& parent); + nmos::resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), const web::json::value& members = web::json::value::array()); // create Root block resource nmos::resource make_root_block(); + + // create Device manager resource + nmos::resource make_device_manager(nc_oid oid, const nmos::settings& settings); + + // create Class manager resource + nmos::resource make_class_manager(nc_oid oid, const nmos::experimental::control_protocol_state& control_protocol_state); + + // add to owner block member + bool add_member(const utility::string_t& child_description, const nmos::resource& child_block, nmos::resource& parent_block); } #endif diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index aceb88ef1..74c671636 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -6,28 +6,98 @@ namespace nmos { namespace experimental { + // create control class property + web::json::value make_control_class_property(const utility::string_t& description, const nc_property_id& id, const utility::string_t& name, const utility::string_t& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) + { + return nmos::details::make_nc_property_descriptor(description, id, name, type_name, is_read_only, is_nullable, is_sequence, is_deprecated, constraints); + } + + namespace details + { + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const web::json::value& fixed_role, const std::vector& properties_, const std::vector& methods_, const std::vector& events_) + { + using web::json::value; + + web::json::value properties = value::array(); + for (const auto& property : properties_) { web::json::push_back(properties, property); } + web::json::value methods = value::array(); + for (const auto& method : methods_) { web::json::push_back(methods, method); } + web::json::value events = value::array(); + for (const auto& event : events_) { web::json::push_back(events, event); } + + return { value::string(description), class_id, name, fixed_role, properties, methods, events }; + } + } + + // create control class with fixed role + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const utility::string_t& fixed_role, const std::vector& properties, const std::vector& methods, const std::vector& events) + { + using web::json::value; + + return details::make_control_class(description, class_id, name, value::string(fixed_role), properties, methods, events); + } + // create control class with no fixed role + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const std::vector& properties, const std::vector& methods, const std::vector& events) + { + using web::json::value; + + return details::make_control_class(description, class_id, name, value::null(), properties, methods, events); + } + + // create control class method parameter + web::json::value make_control_class_method_parameter(const utility::string_t& description, const utility::string_t& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + { + return nmos::details::make_nc_parameter_descriptor(description, name, type_name, is_nullable, is_sequence, constraints); + } + + // create control class method + web::json::value make_control_class_method(const utility::string_t& description, const nc_method_id& id, const utility::string_t& name, const utility::string_t& result_datatype, const std::vector& parameters_, bool is_deprecated) + { + using web::json::value; + + value parameters = value::array(); + for (const auto& parameter : parameters_) { web::json::push_back(parameters, parameter); } + + return nmos::details::make_nc_method_descriptor(description, id, name, result_datatype, parameters, is_deprecated); + } + + // create control class event + web::json::value make_control_class_event(const utility::string_t& description, const nc_event_id& id, const utility::string_t& name, const utility::string_t& event_datatype, bool is_deprecated) + { + return nmos::details::make_nc_event_descriptor(description, id, name, event_datatype, is_deprecated); + } + control_protocol_state::control_protocol_state() { using web::json::value; + auto to_vector = [](const web::json::value& data) + { + if (!data.is_null()) + { + return std::vector(data.as_array().begin(), data.as_array().end()); + } + return std::vector{}; + }; + // setup the core control classes control_classes = { // Control class models // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/#control-class-models-for-branch-v10-dev - { details::make_nc_class_id(details::nc_object_class_id), { value::string(U("NcObject class descriptor")), details::nc_object_class_id, U("NcObject"), value::null(), details::make_nc_object_properties(), details::make_nc_object_methods(), details::make_nc_object_events() } }, - { details::make_nc_class_id(details::nc_block_class_id), { value::string(U("NcBlock class descriptor")), details::nc_block_class_id, U("NcBlock"), value::null(), details::make_nc_block_properties(), details::make_nc_block_methods(), details::make_nc_block_events() } }, - { details::make_nc_class_id(details::nc_worker_class_id), { value::string(U("NcWorker class descriptor")), details::nc_worker_class_id, U("NcWorker"), value::null(), details::make_nc_worker_properties(), details::make_nc_worker_methods(), details::make_nc_worker_events() } }, - { details::make_nc_class_id(details::nc_manager_class_id), { value::string(U("NcManager class descriptor")), details::nc_manager_class_id, U("NcManager"), value::null(), details::make_nc_manager_properties(), details::make_nc_manager_methods(), details::make_nc_manager_events() } }, - { details::make_nc_class_id(details::nc_device_manager_class_id), { value::string(U("NcDeviceManager class descriptor")), details::nc_device_manager_class_id, U("NcDeviceManager"), value::string(U("DeviceManager")), details::make_nc_device_manager_properties(), details::make_nc_device_manager_methods(), details::make_nc_device_manager_events() } }, - { details::make_nc_class_id(details::nc_class_manager_class_id), { value::string(U("NcClassManager class descriptor")), details::nc_class_manager_class_id, U("NcClassManager"), value::string(U("ClassManager")), details::make_nc_class_manager_properties(), details::make_nc_class_manager_methods(), details::make_nc_class_manager_events() } }, + { nmos::details::make_nc_class_id(nc_object_class_id), make_control_class(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), to_vector(make_nc_object_properties()), to_vector(make_nc_object_methods()), to_vector(make_nc_object_events())) }, + { nmos::details::make_nc_class_id(nc_block_class_id), make_control_class(U("NcBlock class descriptor"), nc_block_class_id, U("NcBlock"), to_vector(make_nc_block_properties()), to_vector(make_nc_block_methods()), to_vector(make_nc_block_events())) }, + { nmos::details::make_nc_class_id(nc_worker_class_id), make_control_class(U("NcWorker class descriptor"), nc_worker_class_id, U("NcWorker"), to_vector(make_nc_worker_properties()), to_vector(make_nc_worker_methods()), to_vector(make_nc_worker_events())) }, + { nmos::details::make_nc_class_id(nc_manager_class_id), make_control_class(U("NcManager class descriptor"), nc_manager_class_id, U("NcManager"),to_vector(make_nc_manager_properties()), to_vector(make_nc_manager_methods()), to_vector(make_nc_manager_events())) }, + { nmos::details::make_nc_class_id(nc_device_manager_class_id), make_control_class(U("NcDeviceManager class descriptor"), nc_device_manager_class_id, U("NcDeviceManager"), U("DeviceManager"), to_vector(make_nc_device_manager_properties()), to_vector(make_nc_device_manager_methods()), to_vector(make_nc_device_manager_events())) }, + { nmos::details::make_nc_class_id(nc_class_manager_class_id), make_control_class(U("NcClassManager class descriptor"), nc_class_manager_class_id, U("NcClassManager"), U("ClassManager"), to_vector(make_nc_class_manager_properties()), to_vector(make_nc_class_manager_methods()), to_vector(make_nc_class_manager_events())) }, // identification beacon model // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon - { details::make_nc_class_id(details::nc_ident_beacon_class_id), { value::string(U("NcIdentBeacon class descriptor")), details::nc_ident_beacon_class_id, U("NcIdentBeacon"), value::null(), details::make_nc_ident_beacon_properties(), details::make_nc_ident_beacon_methods(), details::make_nc_ident_beacon_events() } }, + { nmos::details::make_nc_class_id(nc_ident_beacon_class_id), make_control_class(U("NcIdentBeacon class descriptor"), nc_ident_beacon_class_id, U("NcIdentBeacon"), to_vector(make_nc_ident_beacon_properties()), to_vector(make_nc_ident_beacon_methods()), to_vector(make_nc_ident_beacon_events())) }, // Monitoring // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor - { details::make_nc_class_id(details::nc_receiver_monitor_class_id), { value::string(U("NcReceiverMonitor class descriptor")), details::nc_receiver_monitor_class_id, U("NcReceiverMonitor"), value::null(), details::make_nc_receiver_monitor_properties(), details::make_nc_receiver_monitor_methods(), details::make_nc_receiver_monitor_events() } }, - { details::make_nc_class_id(details::nc_receiver_monitor_protected_class_id), { value::string(U("NcReceiverMonitorProtected class descriptor")), details::nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), value::null(), details::make_nc_receiver_monitor_protected_properties(), details::make_nc_receiver_monitor_protected_methods(), details::make_nc_receiver_monitor_protected_events() } } + { nmos::details::make_nc_class_id(nc_receiver_monitor_class_id), make_control_class(U("NcReceiverMonitor class descriptor"), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), to_vector(make_nc_receiver_monitor_properties()), to_vector(make_nc_receiver_monitor_methods()), to_vector(make_nc_receiver_monitor_events())) }, + { nmos::details::make_nc_class_id(nc_receiver_monitor_protected_class_id), make_control_class(U("NcReceiverMonitorProtected class descriptor"), nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), to_vector(make_nc_receiver_monitor_protected_properties()), to_vector(make_nc_receiver_monitor_protected_methods()), to_vector(make_nc_receiver_monitor_protected_events())) } }; // setup the core datatypes @@ -35,69 +105,127 @@ namespace nmos { // Dataype models // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/#datatype-models-for-branch-v10-dev - { U("NcClassId"), {details::make_nc_class_id_datatype()} }, - { U("NcOid"), {details::make_nc_oid_datatype()} }, - { U("NcTouchpoint"), {details::make_nc_touchpoint_datatype()} }, - { U("NcElementId"), {details::make_nc_element_id_datatype()} }, - { U("NcPropertyId"), {details::make_nc_property_id_datatype()} }, - { U("NcPropertyConstraints"), {details::make_nc_property_contraints_datatype()} }, - { U("NcMethodResultPropertyValue"), {details::make_nc_method_result_property_value_datatype()} }, - { U("NcMethodStatus"), {details::make_nc_method_status_datatype()} }, - { U("NcMethodResult"), {details::make_nc_method_result_datatype()} }, - { U("NcId"), {details::make_nc_id_datatype()} }, - { U("NcMethodResultId"), {details::make_nc_method_result_id_datatype()} }, - { U("NcMethodResultLength"), {details::make_nc_method_result_length_datatype()} }, - { U("NcPropertyChangeType"), {details::make_nc_property_change_type_datatype()} }, - { U("NcPropertyChangedEventData"), {details::make_nc_property_changed_event_data_datatype()} }, - { U("NcDescriptor"), {details::make_nc_descriptor_datatype()} }, - { U("NcBlockMemberDescriptor"), {details::make_nc_block_member_descriptor_datatype()} }, - { U("NcMethodResultBlockMemberDescriptors"), {details::make_nc_method_result_block_member_descriptors_datatype()} }, - { U("NcVersionCode"), {details::make_nc_version_code_datatype()} }, - { U("NcOrganizationId"), {details::make_nc_organization_id_datatype()} }, - { U("NcUri"), {details::make_nc_uri_datatype()} }, - { U("NcManufacturer"), {details::make_nc_manufacturer_datatype()} }, - { U("NcUuid"), {details::make_nc_uuid_datatype()} }, - { U("NcProduct"), {details::make_nc_product_datatype()} }, - { U("NcDeviceGenericState"), {details::make_nc_device_generic_state_datatype()} }, - { U("NcDeviceOperationalState"), {details::make_nc_device_operational_state_datatype()} }, - { U("NcResetCause"), {details::make_nc_reset_cause_datatype()} }, - { U("NcName"), {details::make_nc_name_datatype()} }, - { U("NcPropertyDescriptor"), {details::make_nc_property_descriptor_datatype()} }, - { U("NcParameterDescriptor"), {details::make_nc_parameter_descriptor_datatype()} }, - { U("NcMethodId"), {details::make_nc_method_id_datatype()} }, - { U("NcMethodDescriptor"), {details::make_nc_method_descriptor_datatype()} }, - { U("NcEventId"), {details::make_nc_event_id_datatype()} }, - { U("NcEventDescriptor"), {details::make_nc_event_descriptor_datatype()} }, - { U("NcClassDescriptor"), {details::make_nc_class_descriptor_datatype()} }, - { U("NcParameterConstraints"), {details::make_nc_parameter_constraints_datatype()} }, - { U("NcDatatypeType"), {details::make_nc_datatype_type_datatype()} }, - { U("NcDatatypeDescriptor"), {details::make_nc_datatype_descriptor_datatype()} }, - { U("NcMethodResultClassDescriptor"), {details::make_nc_method_result_class_descriptor_datatype()} }, - { U("NcMethodResultDatatypeDescriptor"), {details::make_nc_method_result_datatype_descriptor_datatype()} }, - { U("NcMethodResultError"), {details::make_nc_method_result_error_datatype()} }, - { U("NcDatatypeDescriptorEnum"), {details::make_nc_datatype_descriptor_enum_datatype()} }, - { U("NcDatatypeDescriptorPrimitive"), {details::make_nc_datatype_descriptor_primitive_datatype()} }, - { U("NcDatatypeDescriptorStruct"), {details::make_nc_datatype_descriptor_struct_datatype()} }, - { U("NcDatatypeDescriptorTypeDef"), {details::make_nc_datatype_descriptor_type_def_datatype()} }, - { U("NcEnumItemDescriptor"), {details::make_nc_enum_item_descriptor_datatype()} }, - { U("NcFieldDescriptor"), {details::make_nc_field_descriptor_datatype()} }, - { U("NcPropertyConstraintsNumber"), {details::make_nc_property_constraints_number_datatype()} }, - { U("NcPropertyConstraintsString"), {details::make_nc_property_constraints_string_datatype()} }, - { U("NcRegex"), {details::make_nc_regex_datatype()} }, - { U("NcRolePath"), {details::make_nc_role_path_datatype()} }, - { U("NcParameterConstraintsNumber"), {details::make_nc_parameter_constraints_number_datatype()} }, - { U("NcParameterConstraintsString"), {details::make_nc_parameter_constraints_string_datatype()} }, - { U("NcTimeInterval"), {details::make_nc_time_interval_datatype()} }, - { U("NcTouchpointNmos"), {details::make_nc_touchpoint_nmos_datatype()} }, - { U("NcTouchpointNmosChannelMapping"), {details::make_nc_touchpoint_nmos_channel_mapping_datatype()} }, - { U("NcTouchpointResource"), {details::make_nc_touchpoint_resource_datatype()} }, - { U("NcTouchpointResourceNmos"), {details::make_nc_touchpoint_resource_nmos_datatype()} }, - { U("NcTouchpointResourceNmosChannelMapping"), {details::make_nc_touchpoint_resource_nmos_channel_mapping_datatype()} }, + { U("NcClassId"), {make_nc_class_id_datatype()} }, + { U("NcOid"), {make_nc_oid_datatype()} }, + { U("NcTouchpoint"), {make_nc_touchpoint_datatype()} }, + { U("NcElementId"), {make_nc_element_id_datatype()} }, + { U("NcPropertyId"), {make_nc_property_id_datatype()} }, + { U("NcPropertyConstraints"), {make_nc_property_contraints_datatype()} }, + { U("NcMethodResultPropertyValue"), {make_nc_method_result_property_value_datatype()} }, + { U("NcMethodStatus"), {make_nc_method_status_datatype()} }, + { U("NcMethodResult"), {make_nc_method_result_datatype()} }, + { U("NcId"), {make_nc_id_datatype()} }, + { U("NcMethodResultId"), {make_nc_method_result_id_datatype()} }, + { U("NcMethodResultLength"), {make_nc_method_result_length_datatype()} }, + { U("NcPropertyChangeType"), {make_nc_property_change_type_datatype()} }, + { U("NcPropertyChangedEventData"), {make_nc_property_changed_event_data_datatype()} }, + { U("NcDescriptor"), {make_nc_descriptor_datatype()} }, + { U("NcBlockMemberDescriptor"), {make_nc_block_member_descriptor_datatype()} }, + { U("NcMethodResultBlockMemberDescriptors"), {make_nc_method_result_block_member_descriptors_datatype()} }, + { U("NcVersionCode"), {make_nc_version_code_datatype()} }, + { U("NcOrganizationId"), {make_nc_organization_id_datatype()} }, + { U("NcUri"), {make_nc_uri_datatype()} }, + { U("NcManufacturer"), {make_nc_manufacturer_datatype()} }, + { U("NcUuid"), {make_nc_uuid_datatype()} }, + { U("NcProduct"), {make_nc_product_datatype()} }, + { U("NcDeviceGenericState"), {make_nc_device_generic_state_datatype()} }, + { U("NcDeviceOperationalState"), {make_nc_device_operational_state_datatype()} }, + { U("NcResetCause"), {make_nc_reset_cause_datatype()} }, + { U("NcName"), {make_nc_name_datatype()} }, + { U("NcPropertyDescriptor"), {make_nc_property_descriptor_datatype()} }, + { U("NcParameterDescriptor"), {make_nc_parameter_descriptor_datatype()} }, + { U("NcMethodId"), {make_nc_method_id_datatype()} }, + { U("NcMethodDescriptor"), {make_nc_method_descriptor_datatype()} }, + { U("NcEventId"), {make_nc_event_id_datatype()} }, + { U("NcEventDescriptor"), {make_nc_event_descriptor_datatype()} }, + { U("NcClassDescriptor"), {make_nc_class_descriptor_datatype()} }, + { U("NcParameterConstraints"), {make_nc_parameter_constraints_datatype()} }, + { U("NcDatatypeType"), {make_nc_datatype_type_datatype()} }, + { U("NcDatatypeDescriptor"), {make_nc_datatype_descriptor_datatype()} }, + { U("NcMethodResultClassDescriptor"), {make_nc_method_result_class_descriptor_datatype()} }, + { U("NcMethodResultDatatypeDescriptor"), {make_nc_method_result_datatype_descriptor_datatype()} }, + { U("NcMethodResultError"), {make_nc_method_result_error_datatype()} }, + { U("NcDatatypeDescriptorEnum"), {make_nc_datatype_descriptor_enum_datatype()} }, + { U("NcDatatypeDescriptorPrimitive"), {make_nc_datatype_descriptor_primitive_datatype()} }, + { U("NcDatatypeDescriptorStruct"), {make_nc_datatype_descriptor_struct_datatype()} }, + { U("NcDatatypeDescriptorTypeDef"), {make_nc_datatype_descriptor_type_def_datatype()} }, + { U("NcEnumItemDescriptor"), {make_nc_enum_item_descriptor_datatype()} }, + { U("NcFieldDescriptor"), {make_nc_field_descriptor_datatype()} }, + { U("NcPropertyConstraintsNumber"), {make_nc_property_constraints_number_datatype()} }, + { U("NcPropertyConstraintsString"), {make_nc_property_constraints_string_datatype()} }, + { U("NcRegex"), {make_nc_regex_datatype()} }, + { U("NcRolePath"), {make_nc_role_path_datatype()} }, + { U("NcParameterConstraintsNumber"), {make_nc_parameter_constraints_number_datatype()} }, + { U("NcParameterConstraintsString"), {make_nc_parameter_constraints_string_datatype()} }, + { U("NcTimeInterval"), {make_nc_time_interval_datatype()} }, + { U("NcTouchpointNmos"), {make_nc_touchpoint_nmos_datatype()} }, + { U("NcTouchpointNmosChannelMapping"), {make_nc_touchpoint_nmos_channel_mapping_datatype()} }, + { U("NcTouchpointResource"), {make_nc_touchpoint_resource_datatype()} }, + { U("NcTouchpointResourceNmos"), {make_nc_touchpoint_resource_nmos_datatype()} }, + { U("NcTouchpointResourceNmosChannelMapping"), {make_nc_touchpoint_resource_nmos_channel_mapping_datatype()} }, // Monitoring // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#datatypes - { U("NcConnectionStatus"), {details::make_nc_connection_status_datatype()} }, - { U("NcPayloadStatus"), {details::make_nc_payload_status_datatype()} } + { U("NcConnectionStatus"), {make_nc_connection_status_datatype()} }, + { U("NcPayloadStatus"), {make_nc_payload_status_datatype()} } }; } + + // insert control class, false if class already presented + bool control_protocol_state::insert(const experimental::control_class& control_class) + { + const auto& class_id = nmos::details::make_nc_class_id(control_class.class_id); + + auto lock = write_lock(); + + if (control_classes.end() == control_classes.find(class_id)) + { + control_classes[nmos::details::make_nc_class_id(control_class.class_id)] = control_class; + return true; + } + return false; + } + + // erase control class of the given class id, false if the required class not found + bool control_protocol_state::erase(nc_class_id class_id_) + { + const auto& class_id = nmos::details::make_nc_class_id(class_id_); + + auto lock = write_lock(); + + if (control_classes.end() != control_classes.find(class_id)) + { + control_classes.erase(class_id); + return true; + } + return false; + } + + // insert datatype, false if datatype already presented + bool control_protocol_state::insert(const experimental::datatype& datatype) + { + const auto& name = nmos::fields::nc::name(datatype.descriptor); + + auto lock = write_lock(); + + if (datatypes.end() == datatypes.find(name)) + { + datatypes[name] = datatype; + return true; + } + return false; + } + + // erase datatype of the given datatype name, false if the required datatype not found + bool control_protocol_state::erase(const utility::string_t& name) + { + auto lock = write_lock(); + + if (datatypes.end() != datatypes.find(name)) + { + datatypes.erase(name); + return true; + } + return false; + } } } \ No newline at end of file diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index 82f4e755f..ddf68e906 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -3,7 +3,7 @@ #include #include "cpprest/json_utils.h" -#include "nmos/control_protocol_class_id.h" // for nmos::details::nc_class_id definitions +#include "nmos/control_protocol_typedefs.h" #include "nmos/mutex.h" #include "nmos/resources.h" @@ -16,11 +16,11 @@ namespace nmos struct control_class // NcClassDescriptor { web::json::value description; - nmos::details::nc_class_id class_id; + nmos::nc_class_id class_id; utility::string_t name; web::json::value fixed_role; - web::json::value properties; // array of nc_property_descriptor + web::json::value properties; // array of nc_property_descriptor web::json::value methods; // array of nc_method_descriptor web::json::value events; // array of nc_event_descriptor }; @@ -51,7 +51,38 @@ namespace nmos nmos::write_lock write_lock() const { return nmos::write_lock{ mutex }; } control_protocol_state(); + + // insert control class, false if class already presented + bool insert(const experimental::control_class& control_class); + // erase control class of the given class id, false if the required class not found + bool erase(nc_class_id class_id); + + // insert datatype, false if datatype already presented + bool insert(const experimental::datatype& datatype); + // erase datatype of the given datatype name, false if the required datatype not found + bool erase(const utility::string_t& name); }; + + // helper functions to create non-standard control class + // + // create control class method parameter + web::json::value make_control_class_method_parameter(const utility::string_t& description, const utility::string_t& name, const utility::string_t& type_name, + bool is_nullable = false, bool is_sequence = false, const web::json::value& constraints = web::json::value::null()); + // create control class method + web::json::value make_control_class_method(const utility::string_t& description, const nc_method_id& id, const utility::string_t& name, const utility::string_t& result_datatype, + const std::vector& parameters = {}, bool is_deprecated = false); + + // create control class event + web::json::value make_control_class_event(const utility::string_t& description, const nc_event_id& id, const utility::string_t& name, const utility::string_t& event_datatype, + bool is_deprecated = false); + + // create control class property + web::json::value make_control_class_property(const utility::string_t& description, const nc_property_id& id, const utility::string_t& name, const utility::string_t& type_name, + bool is_read_only = false, bool is_nullable = false, bool is_sequence = false, bool is_deprecated = false, const web::json::value& constraints = web::json::value::null()); + // create control class with fixed role + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const utility::string_t& fixed_role, const std::vector& properties, const std::vector& methods, const std::vector& events); + // create control class with no fixed role + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const std::vector& properties, const std::vector& methods, const std::vector& events); } } diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h new file mode 100644 index 000000000..310a16af4 --- /dev/null +++ b/Development/nmos/control_protocol_typedefs.h @@ -0,0 +1,183 @@ +#ifndef NMOS_CONTROL_PROTOCOL_TYPEDEFS_H +#define NMOS_CONTROL_PROTOCOL_TYPEDEFS_H + +#include "cpprest/basic_utils.h" + +namespace web +{ + namespace json + { + class value; + } +} + +namespace nmos +{ + namespace nc_message_type + { + enum type + { + command = 0, + command_response = 1, + notification = 2, + subscription = 3, + subscription_response = 4, + error = 5 + }; + } + + // Method invokation status + namespace nc_method_status + { + enum status + { + ok = 200, // Method call was successful + property_deprecated = 298, // Method call was successful but targeted property is deprecated + method_deprecated = 299, // Method call was successful but method is deprecated + bad_command_format = 400, // Badly-formed command + unathorized = 401, // Client is not authorized + bad_oid = 404, // Command addresses a nonexistent object + read_only = 405, // Attempt to change read-only state + invalid_request = 406, // Method call is invalid in current operating context + conflict = 409, // There is a conflict with the current state of the device + buffer_overflow = 413, // Something was too big + index_out_of_bounds = 414, // Index is outside the available range + parameter_error = 417, // Method parameter does not meet expectations + locked = 423, // Addressed object is locked + device_error = 500, // Internal device error + method_not_implemented = 501, // Addressed method is not implemented by the addressed object + property_not_implemented = 502, // Addressed property is not implemented by the addressed object + not_ready = 503, // The device is not ready to handle any commands + timeout = 504, // Method call did not finish within the allotted time + property_version_error = 505 // Incompatible protocol version + }; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodresult + struct nc_method_result + { + nc_method_status::status status; + }; + + // Datatype type + namespace nc_datatype_type + { + enum type + { + Primitive = 0, + Typedef = 1, + Struct = 2, + Enum = 3 + }; + } + + // Device generic operational state + namespace nc_device_generic_state + { + enum state + { + unknown = 0, // Unknown + normal_operation = 1, // Normal operation + initializing = 2, // Device is initializing + updating = 3, // Device is performing a software or firmware update + licensing_error = 4, // Device is experiencing a licensing error + internal_error = 5 // Device is experiencing an internal error + }; + } + + // Reset cause enum + namespace nc_reset_cause + { + enum cause + { + unknown = 0, // Unknown + power_on = 1, // Power on + internal_error = 2, // Internal error + upgrade = 3, // Upgrade + controller_request = 4, // Controller request + manual_reset = 5 // Manual request from the front panel + }; + } + + // NcConnectionStatus + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncconnectionstatus + namespace nc_connection_status + { + enum status + { + undefined = 0, // This is the value when there is no receiver + connected = 1, // Connected to a stream + disconnected = 2, // Not connected to a stream + connection_error = 3 // A connection error was encountered + }; + } + + // NcPayloadStatus + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncpayloadstatus + namespace nc_payload_status + { + enum status + { + undefined = 0, // This is the value when there's no connection. + payload_ok = 1, // Payload is being received without errors and is the correct type + payload_format_unsupported = 2, // Payload is being received but is of an unsupported type + payloadError = 3 // A payload error was encountered + }; + } + + // NcElementId + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid + struct nc_element_id + { + uint16_t level; + uint16_t index; + }; + + // NcEventId + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventid + typedef nc_element_id nc_event_id; + + // NcMethodId + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodid + typedef nc_element_id nc_method_id; + + // NcPropertyId + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertyid + typedef nc_element_id nc_property_id; + + // NcId + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncid + typedef uint32_t nc_id; + + // NcOid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncoid + typedef uint32_t nc_oid; + const nc_oid root_block_oid{ 1 }; + + // NcUri + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncuri + typedef utility::string_t nc_uri; + + // NcUuid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncuuid + typedef utility::string_t nc_uuid; + + // NcClassId + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid + typedef std::vector nc_class_id; + const nc_class_id nc_object_class_id({ 1 }); + const nc_class_id nc_block_class_id({ 1, 1 }); + const nc_class_id nc_worker_class_id({ 1, 2 }); + const nc_class_id nc_manager_class_id({ 1, 3 }); + const nc_class_id nc_device_manager_class_id({ 1, 3, 1 }); + const nc_class_id nc_class_manager_class_id({ 1, 3, 2 }); + const nc_class_id nc_ident_beacon_class_id({ 1, 2, 2 }); + const nc_class_id nc_receiver_monitor_class_id({ 1, 2, 3 }); + const nc_class_id nc_receiver_monitor_protected_class_id({ 1, 2, 3, 1 }); + + // NcTouchpoint + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nctouchpoint + typedef utility::string_t nc_touch_point; +} + +#endif diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index a19f58699..3db423168 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -4,7 +4,8 @@ #include #include #include "cpprest/json_utils.h" -#include "nmos/control_protocol_resource.h" // for nc_object_class_id, nc_manager_class_id, nc_device_manager_class_id, nc_class_manager_class_id +#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_typedefs.h" #include "nmos/json_fields.h" #include "nmos/resources.h" @@ -22,34 +23,34 @@ namespace nmos } return control_class_id == class_id; } + } - bool is_nc_block(const nc_class_id& class_id) - { - return is_control_class(nc_object_class_id, class_id); - } + bool is_nc_block(const nc_class_id& class_id) + { + return details::is_control_class(nc_object_class_id, class_id); + } - bool is_nc_manager(const nc_class_id& class_id) - { - return is_control_class(nc_manager_class_id, class_id); - } + bool is_nc_manager(const nc_class_id& class_id) + { + return details::is_control_class(nc_manager_class_id, class_id); + } - bool is_nc_device_manager(const nc_class_id& class_id) - { - return is_control_class(nc_device_manager_class_id, class_id); - } + bool is_nc_device_manager(const nc_class_id& class_id) + { + return details::is_control_class(nc_device_manager_class_id, class_id); + } - bool is_nc_class_manager(const nc_class_id& class_id) - { - return is_control_class(nc_class_manager_class_id, class_id); - } + bool is_nc_class_manager(const nc_class_id& class_id) + { + return details::is_control_class(nc_class_manager_class_id, class_id); + } - nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const nc_class_id& suffix) - { - nc_class_id class_id = prefix; - class_id.push_back(authority_key); - class_id.insert(class_id.end(), suffix.begin(), suffix.end()); - return class_id; - } + nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const nc_class_id& suffix) + { + nc_class_id class_id = prefix; + class_id.push_back(authority_key); + class_id.insert(class_id.end(), suffix.begin(), suffix.end()); + return class_id; } void get_member_descriptors(const resources& resources, resources::iterator resource, bool recurse, web::json::array& descriptors) @@ -69,7 +70,7 @@ namespace nmos // get members on all NcBlock(s) for (const auto& member : members) { - if (details::is_nc_block(details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + if (is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(member); @@ -119,7 +120,7 @@ namespace nmos // do role match on all NcBlock(s) for (const auto& member : members) { - if (details::is_nc_block(details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + if (is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(member); @@ -131,7 +132,7 @@ namespace nmos } } - void find_members_by_class_id(const resources& resources, resources::iterator resource, const details::nc_class_id& class_id_, bool include_derived, bool recurse, web::json::array& descriptors) + void find_members_by_class_id(const resources& resources, resources::iterator resource, const nc_class_id& class_id_, bool include_derived, bool recurse, web::json::array& descriptors) { auto find_members_by_matching_class_id = [&](const web::json::array& members) { @@ -139,7 +140,7 @@ namespace nmos auto match = [&](const web::json::value& descriptor) { - const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); + const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); if (include_derived) { return !boost::find_first(class_id, class_id_).empty(); } else { return class_id == class_id_; } @@ -163,7 +164,7 @@ namespace nmos // do class_id match on all NcBlock(s) for (const auto& member : members) { - if (details::is_nc_block(details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + if (is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(member); diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index ec59aa747..acb4a7c76 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -2,29 +2,26 @@ #define NMOS_CONTROL_PROTOCOL_UTILS_H #include "cpprest/basic_utils.h" -#include "nmos/control_protocol_class_id.h" // for nc_class_id definition +#include "nmos/control_protocol_typedefs.h" // for nc_class_id definition #include "nmos/resources.h" namespace nmos { - namespace details - { - bool is_nc_block(const nc_class_id& class_id); + bool is_nc_block(const nc_class_id& class_id); - bool is_nc_manager(const nc_class_id& class_id); + bool is_nc_manager(const nc_class_id& class_id); - bool is_nc_device_manager(const nc_class_id& class_id); + bool is_nc_device_manager(const nc_class_id& class_id); - bool is_nc_class_manager(const nc_class_id& class_id); + bool is_nc_class_manager(const nc_class_id& class_id); - nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const nc_class_id& suffix); - } + nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const nc_class_id& suffix); void get_member_descriptors(const resources& resources, resources::iterator resource, bool recurse, web::json::array& descriptors); void find_members_by_role(const resources& resources, resources::iterator resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& nc_block_member_descriptors); - void find_members_by_class_id(const resources& resources, resources::iterator resource, const details::nc_class_id& class_id, bool include_derived, bool recurse, web::json::array& descriptors); + void find_members_by_class_id(const resources& resources, resources::iterator resource, const nc_class_id& class_id, bool include_derived, bool recurse, web::json::array& descriptors); } #endif diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 1dabbef21..9e9c80784 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -5,6 +5,7 @@ #include "cpprest/regex_utils.h" #include "nmos/api_utils.h" #include "nmos/control_protocol_resources.h" +#include "nmos/control_protocol_resource.h" #include "nmos/control_protocol_state.h" #include "nmos/control_protocol_utils.h" #include "nmos/is12_versions.h" @@ -56,7 +57,7 @@ namespace nmos while (!class_id.empty()) { - auto class_found = control_classes.find(make_nc_class_id(class_id)); + auto class_found = control_classes.find(nmos::details::make_nc_class_id(class_id)); if (control_classes.end() != class_found) { auto& properties = class_found->second.properties.as_array(); @@ -88,19 +89,19 @@ namespace nmos const auto& property_id = nmos::fields::nc::id(arguments); - slog::log(gate, SLOG_FLF) << "Get property: " << property_id.to_string(); + slog::log(gate, SLOG_FLF) << "Get property: " << property_id.serialize(); // find the relevant nc_property_descriptor const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); if (!property.is_null()) { - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(property))); + return make_control_protocol_response(handle, { nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(property))); } // unknown property utility::stringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do Get"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); }; // Set property value const auto set = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) @@ -110,7 +111,7 @@ namespace nmos const auto& property_id = nmos::fields::nc::id(arguments); const auto& val = nmos::fields::nc::value(arguments); - slog::log(gate, SLOG_FLF) << "Set property: " << property_id.to_string() << " value: " << val.to_string(); + slog::log(gate, SLOG_FLF) << "Set property: " << property_id.serialize() << " value: " << val.serialize(); // find the relevant nc_property_descriptor const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); @@ -118,13 +119,13 @@ namespace nmos { if (nmos::fields::nc::is_read_only(property)) { - return details::make_control_protocol_response(handle, { details::nc_method_status::read_only }); + return make_control_protocol_response(handle, { nc_method_status::read_only }); } if ((val.is_null() && !nmos::fields::nc::is_nullable(property)) || (val.is_array() && !nmos::fields::nc::is_sequence(property))) { - return details::make_control_protocol_response(handle, { details::nc_method_status::parameter_error }); + return make_control_protocol_response(handle, { nc_method_status::parameter_error }); } resources.modify(resource, [&](nmos::resource& resource) @@ -133,13 +134,13 @@ namespace nmos resource.updated = strictly_increasing_update(resources); }); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); + return make_control_protocol_response(handle, { nc_method_status::ok }); } // unknown property utility::stringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << " to do Set"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); }; // Get sequence item const auto get_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) @@ -149,7 +150,7 @@ namespace nmos const auto& property_id = nmos::fields::nc::id(arguments); const auto& index = nmos::fields::nc::index(arguments); - slog::log(gate, SLOG_FLF) << "Get sequence item: " << property_id.to_string() << " index: " << index; + slog::log(gate, SLOG_FLF) << "Get sequence item: " << property_id.serialize() << " index: " << index; // find the relevant nc_property_descriptor const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); @@ -160,26 +161,26 @@ namespace nmos // property is not a sequence utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); } auto& data = resource->data.at(nmos::fields::nc::name(property)); if (!data.is_null() && data.as_array().size() > (size_t)index) { - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, data.at(index)); + return make_control_protocol_response(handle, { nc_method_status::ok }, data.at(index)); } // out of bound utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do GetSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); + return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); } // unknown property utility::stringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do GetSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); }; // Set sequence item const auto set_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) @@ -190,7 +191,7 @@ namespace nmos const auto& index = nmos::fields::nc::index(arguments); const auto& val = nmos::fields::nc::value(arguments); - slog::log(gate, SLOG_FLF) << "Set sequence item: " << property_id.to_string() << " index: " << index << " value: " << val.to_string(); + slog::log(gate, SLOG_FLF) << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize(); // find the relevant nc_property_descriptor const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); @@ -201,7 +202,7 @@ namespace nmos // property is not a sequence utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do SetSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); } auto& data = resource->data.at(nmos::fields::nc::name(property)); @@ -214,19 +215,19 @@ namespace nmos resource.updated = strictly_increasing_update(resources); }); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); + return make_control_protocol_response(handle, { nc_method_status::ok }); } // out of bound utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do SetSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); + return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); } // unknown property utility::stringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do SetSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); }; // Add item to sequence const auto add_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) @@ -236,7 +237,7 @@ namespace nmos const auto& property_id = nmos::fields::nc::id(arguments); const auto& val = nmos::fields::nc::value(arguments); - slog::log(gate, SLOG_FLF) << "Add sequence item: " << property_id.to_string() << " value: " << val.to_string(); + slog::log(gate, SLOG_FLF) << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize(); // find the relevant nc_property_descriptor const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); @@ -247,7 +248,7 @@ namespace nmos // property is not a sequence utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do AddSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); } auto& data = resource->data.at(nmos::fields::nc::name(property)); @@ -260,13 +261,13 @@ namespace nmos resource.updated = strictly_increasing_update(resources); }); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, uint32_t(data.as_array().size() - 1)); + return make_control_protocol_response(handle, { nc_method_status::ok }, uint32_t(data.as_array().size() - 1)); } // unknown property utility::stringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do AddSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); }; // Delete sequence item const auto remove_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) @@ -276,7 +277,7 @@ namespace nmos const auto& property_id = nmos::fields::nc::id(arguments); const auto& index = nmos::fields::nc::index(arguments); - slog::log(gate, SLOG_FLF) << "Remove sequence item: " << property_id.to_string() << " index: " << index; + slog::log(gate, SLOG_FLF) << "Remove sequence item: " << property_id.serialize() << " index: " << index; // find the relevant nc_property_descriptor const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); @@ -287,7 +288,7 @@ namespace nmos // property is not a sequence utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do RemoveSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); } auto& data = resource->data.at(nmos::fields::nc::name(property)); @@ -295,25 +296,25 @@ namespace nmos if (!data.is_null() && data.as_array().size() > (size_t)index) { resources.modify(resource, [&](nmos::resource& resource) - { - auto& sequence = resource.data[nmos::fields::nc::name(property)].as_array(); - sequence.erase(index); + { + auto& sequence = resource.data[nmos::fields::nc::name(property)].as_array(); + sequence.erase(index); - resource.updated = strictly_increasing_update(resources); - }); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }); + resource.updated = strictly_increasing_update(resources); + }); + return make_control_protocol_response(handle, { nc_method_status::ok }); } // out of bound utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do RemoveSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::index_out_of_bounds }, ss.str()); + return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); } // unknown property utility::stringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do RemoveSequenceItem"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); }; // Get sequence length const auto get_sequence_length = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) @@ -322,7 +323,7 @@ namespace nmos const auto& property_id = nmos::fields::nc::id(arguments); - slog::log(gate, SLOG_FLF) << "Get sequence length: " << property_id.to_string(); + slog::log(gate, SLOG_FLF) << "Get sequence length: " << property_id.serialize(); // find the relevant nc_property_descriptor const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); @@ -333,7 +334,7 @@ namespace nmos // property is not a sequence utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceLength"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); } auto& data = resource->data.at(nmos::fields::nc::name(property)); @@ -344,7 +345,7 @@ namespace nmos if (data.is_null()) { // null - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, value::null()); + return make_control_protocol_response(handle, { nc_method_status::ok }, value::null()); } } else @@ -355,16 +356,16 @@ namespace nmos // null utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << " is a null sequence to do GetSequenceLength"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::invalid_request }, ss.str()); + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); } } - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, uint32_t(data.as_array().size())); + return make_control_protocol_response(handle, { nc_method_status::ok }, uint32_t(data.as_array().size())); } // unknown property utility::stringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << " to do GetSequenceLength"; - return details::make_control_protocol_error_response(handle, { details::nc_method_status::property_not_implemented }, ss.str()); + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); }; // NcBlock methods implementation @@ -380,7 +381,7 @@ namespace nmos auto descriptors = value::array(); nmos::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptors); + return make_control_protocol_response(handle, { nc_method_status::ok }, descriptors); }; // Finds member(s) by path const auto find_members_by_path = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes&, const nmos::experimental::datatypes&, slog::base_gate& gate) @@ -390,12 +391,12 @@ namespace nmos // Relative path to search for (MUST not include the role of the block targeted by oid) const auto& path = arguments.at(nmos::fields::nc::path); - slog::log(gate, SLOG_FLF) << "Find member(s) by path: " << "path: " << path.to_string(); + slog::log(gate, SLOG_FLF) << "Find member(s) by path: " << "path: " << path.serialize(); if (0 == path.size()) { // empty path - return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty path to do FindMembersByPath")); + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty path to do FindMembersByPath")); } auto nc_block_member_descriptors = value::array(); @@ -424,18 +425,18 @@ namespace nmos // no role utility::stringstream_t ss; ss << U("role: ") << role.as_string() << U(" not found to do FindMembersByPath"); - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str()); + return make_control_protocol_error_response(handle, { nc_method_status::bad_oid }, ss.str()); } } else { // no members - return details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, U("no members to do FindMembersByPath")); + return make_control_protocol_error_response(handle, { nc_method_status::bad_oid }, U("no members to do FindMembersByPath")); } } web::json::push_back(nc_block_member_descriptors, nc_block_member_descriptor); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, nc_block_member_descriptors); + return make_control_protocol_response(handle, { nc_method_status::ok }, nc_block_member_descriptors); }; // Finds members with given role name or fragment const auto find_members_by_role = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes&, const nmos::experimental::datatypes&, slog::base_gate& gate) @@ -452,29 +453,29 @@ namespace nmos if (role.empty()) { // empty role - return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty role to do FindMembersByRole")); + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty role to do FindMembersByRole")); } auto descriptors = value::array(); nmos::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptors); + return make_control_protocol_response(handle, { nc_method_status::ok }, descriptors); }; // Finds members with given class id const auto find_members_by_class_id = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes&, const nmos::experimental::datatypes&, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for + const auto& class_id = parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for const auto& include_derived = nmos::fields::nc::include_derived(arguments); // If TRUE it will also include derived class descriptors const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks - slog::log(gate, SLOG_FLF) << "Find members with given class id: " << "class_id: " << details::make_nc_class_id(class_id).to_string(); + slog::log(gate, SLOG_FLF) << "Find members with given class id: " << "class_id: " << nmos::details::make_nc_class_id(class_id).serialize(); if (class_id.empty()) { // empty class_id - return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty classId to do FindMembersByClassId")); + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty classId to do FindMembersByClassId")); } // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -482,27 +483,27 @@ namespace nmos auto descriptors = value::array(); nmos::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptors); + return make_control_protocol_response(handle, { nc_method_status::ok }, descriptors); }; // NcClassManager methods implementation // Get a single class descriptor const auto get_control_class = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) { - const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for + const auto& class_id = parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements - slog::log(gate, SLOG_FLF) << "Get a single class descriptor: " << "class_id: " << details::make_nc_class_id(class_id).to_string(); + slog::log(gate, SLOG_FLF) << "Get a single class descriptor: " << "class_id: " << nmos::details::make_nc_class_id(class_id).serialize(); if (class_id.empty()) { // empty class_id - return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty classId to do GetControlClass")); + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty classId to do GetControlClass")); } // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - auto class_found = control_classes.find(make_nc_class_id(class_id)); + auto class_found = control_classes.find(nmos::details::make_nc_class_id(class_id)); if (control_classes.end() != class_found) { @@ -521,7 +522,7 @@ namespace nmos { while (!id.empty()) { - auto found = control_classes.find(make_nc_class_id(id)); + auto found = control_classes.find(nmos::details::make_nc_class_id(id)); if (control_classes.end() != found) { for (const auto& property : found->second.properties.as_array()) { web::json::push_back(properties, property); } @@ -533,10 +534,10 @@ namespace nmos } auto descriptor = details::make_nc_class_descriptor(description, class_id, name, fixed_role, properties, methods, events); - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptor); + return make_control_protocol_response(handle, { nc_method_status::ok }, descriptor); } - return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("classId not found")); + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("classId not found")); }; // Get a single datatype descriptor const auto get_datatype = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes&, const nmos::experimental::datatypes& datatypes, slog::base_gate& gate) @@ -551,7 +552,7 @@ namespace nmos if (name.empty()) { // empty name - return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("empty name to do GetDatatype")); + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty name to do GetDatatype")); } auto datatype_found = datatypes.find(name); @@ -563,7 +564,7 @@ namespace nmos if (include_inherited) { const auto& type = nmos::fields::nc::type(descriptor); - if (details::nc_datatype_type::Struct == type) + if (nc_datatype_type::Struct == type) { auto descriptor_ = descriptor; @@ -591,10 +592,10 @@ namespace nmos } } - return details::make_control_protocol_response(handle, { details::nc_method_status::ok }, descriptor); + return make_control_protocol_response(handle, { nc_method_status::ok }, descriptor); } - return details::make_control_protocol_error_response(handle, { details::nc_method_status::parameter_error }, U("name not found")); + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("name not found")); }; // method handlers for the different classes @@ -640,16 +641,16 @@ namespace nmos // hmm, todo, custom class and assoicated methods will need to be inserted within follwoing table! const std::map methods = { - { details::make_nc_class_id(details::nc_object_class_id), nc_object_method_handlers }, - { details::make_nc_class_id(details::nc_block_class_id), nc_block_method_handlers }, - { details::make_nc_class_id(details::nc_class_manager_class_id), nc_class_manager_method_handlers } + { nmos::details::make_nc_class_id(nc_object_class_id), nc_object_method_handlers }, + { nmos::details::make_nc_class_id(nc_block_class_id), nc_block_method_handlers }, + { nmos::details::make_nc_class_id(nc_class_manager_class_id), nc_class_manager_method_handlers } }; auto class_id = class_id_; while (!class_id.empty()) { - auto subset_methods_found = methods.find(make_nc_class_id(class_id)); + auto subset_methods_found = methods.find(nmos::details::make_nc_class_id(class_id)); if (methods.end() != subset_methods_found) { @@ -711,7 +712,7 @@ namespace nmos const auto ws_href = web::uri_builder() .set_scheme(web::ws_scheme(secure)) .set_host(nmos::get_host(model.settings)) - .set_port(nmos::fields::events_ws_port(model.settings)) + .set_port(nmos::fields::control_protocol_ws_port(model.settings)) .set_path(ws_ncp_path) .to_uri(); @@ -844,7 +845,7 @@ namespace nmos const auto msg_type = nmos::fields::nc::message_type(message); switch (msg_type) { - case details::nc_message_type::command: + case nc_message_type::command: { // validate command-message details::validate_controlprotocolapi_command_message_schema(version, message); @@ -865,7 +866,7 @@ namespace nmos auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != resource) { - auto class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); + auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); // find the relevent method handler to execute auto method = details::find_method(method_id, class_id, get_control_protocol_classes()); @@ -879,7 +880,7 @@ namespace nmos utility::stringstream_t ss; ss << U("unsupported method id: ") << method_id.serialize(); web::json::push_back(responses, - details::make_control_protocol_error_response(handle, { details::nc_method_status::method_not_implemented }, ss.str())); + make_control_protocol_error_response(handle, { nc_method_status::method_not_implemented }, ss.str())); } } else @@ -888,7 +889,7 @@ namespace nmos utility::stringstream_t ss; ss << U("unknown oid: ") << oid; web::json::push_back(responses, - details::make_control_protocol_error_response(handle, { details::nc_method_status::bad_oid }, ss.str())); + make_control_protocol_error_response(handle, { nc_method_status::bad_oid }, ss.str())); } } @@ -896,13 +897,13 @@ namespace nmos resources.modify(grain, [&](nmos::resource& grain) { web::json::push_back(nmos::fields::message_grain_data(grain.data), - details::make_control_protocol_message_response(details::nc_message_type::command_response, responses)); + make_control_protocol_message_response(nc_message_type::command_response, responses)); grain.updated = strictly_increasing_update(resources); }); } break; - case details::nc_message_type::subscription: + case nc_message_type::subscription: { // hmm, todo... } @@ -920,7 +921,7 @@ namespace nmos resources.modify(grain, [&](nmos::resource& grain) { web::json::push_back(nmos::fields::message_grain_data(grain.data), - details::make_control_protocol_error_message({ details::nc_method_status::bad_command_format }, utility::s2us(e.what()))); + make_control_protocol_error_message({ nc_method_status::bad_command_format }, utility::s2us(e.what()))); grain.updated = strictly_increasing_update(resources); }); @@ -932,8 +933,7 @@ namespace nmos resources.modify(grain, [&](nmos::resource& grain) { web::json::push_back(nmos::fields::message_grain_data(grain.data), - details::make_control_protocol_error_message({ details::nc_method_status::bad_command_format }, - utility::s2us(std::string("Unexpected exception while handing control protocol command : ") + e.what()))); + make_control_protocol_error_message({ nc_method_status::bad_command_format }, utility::s2us(std::string("Unexpected exception while handing control protocol command : ") + e.what()))); grain.updated = strictly_increasing_update(resources); }); @@ -945,8 +945,7 @@ namespace nmos resources.modify(grain, [&](nmos::resource& grain) { web::json::push_back(nmos::fields::message_grain_data(grain.data), - details::make_control_protocol_error_message({ details::nc_method_status::bad_command_format }, - U("Unexpected unknown exception while handing control protocol command"))); + make_control_protocol_error_message({ nc_method_status::bad_command_format }, U("Unexpected unknown exception while handing control protocol command"))); grain.updated = strictly_increasing_update(resources); }); From 901780a795fe863675718a950b97dabee6889ea3 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 29 Aug 2023 19:13:27 +0100 Subject: [PATCH 034/250] Use of nc_class_id struct and method_id struct for map key --- .../nmos/control_protocol_handlers.cpp | 6 +-- .../nmos/control_protocol_resource.cpp | 20 ++++++- Development/nmos/control_protocol_resource.h | 10 ++-- Development/nmos/control_protocol_state.cpp | 28 +++++----- Development/nmos/control_protocol_state.h | 4 +- Development/nmos/control_protocol_typedefs.h | 10 ++++ Development/nmos/control_protocol_ws_api.cpp | 54 +++++++++++++------ 7 files changed, 89 insertions(+), 43 deletions(-) diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index 1118a35b8..e60f387cb 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -25,15 +25,13 @@ namespace nmos auto lock = control_protocol_state.write_lock(); - auto class_id_data = details::make_nc_class_id(class_id); - auto& control_classes = control_protocol_state.control_classes; - if (control_classes.end() == control_classes.find(class_id_data)) + if (control_classes.end() == control_classes.find(class_id)) { return false; } - control_classes[class_id_data] = control_class; + control_classes[class_id] = control_class; return true; }; } diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 4ab09f694..12d8da0a8 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -42,9 +42,13 @@ namespace nmos }); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid - web::json::value make_nc_element_id(const nc_element_id& element_id) + web::json::value make_nc_element_id(const nc_element_id& id) { - return make_nc_element_id(element_id.level, element_id.index); + return make_nc_element_id(id.level, id.index); + } + nc_element_id parse_nc_element_id(const web::json::value& id) + { + return { uint16_t(nmos::fields::nc::level(id)), uint16_t(nmos::fields::nc::index(id)) }; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventid @@ -52,18 +56,30 @@ namespace nmos { return make_nc_element_id(id); } + nc_event_id parse_nc_event_id(const web::json::value& id) + { + return parse_nc_element_id(id); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodid web::json::value make_nc_method_id(const nc_method_id& id) { return make_nc_element_id(id); } + nc_event_id parse_nc_method_id(const web::json::value& id) + { + return parse_nc_element_id(id); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertyid web::json::value make_nc_property_id(const nc_property_id& id) { return make_nc_element_id(id); } + nc_event_id parse_nc_property_id(const web::json::value& id) + { + return parse_nc_element_id(id); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid web::json::value make_nc_class_id(const nc_class_id& class_id) diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 8d2c4208b..684a0eb44 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -24,18 +24,20 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid //web::json::value make_nc_element_id(uint16_t level, uint16_t index); web::json::value make_nc_element_id(const nc_element_id& element_id); + nc_element_id parse_nc_element_id(const web::json::value& element_id); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventid //web::json::value make_nc_event_id(uint16_t level, uint16_t index); web::json::value make_nc_event_id(const nc_event_id& event_id); + nc_event_id parse_nc_event_id(const web::json::value& event_id); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodid - //web::json::value make_nc_method_id(uint16_t level, uint16_t index); - web::json::value make_nc_method_id(const nc_method_id& event_id); + web::json::value make_nc_method_id(const nc_method_id& method_id); + nc_method_id parse_nc_method_id(const web::json::value& method_id); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertyid - //web::json::value make_nc_property_id(uint16_t level, uint16_t index); - web::json::value make_nc_property_id(const nc_property_id& event_id); + web::json::value make_nc_property_id(const nc_property_id& property_id); + nc_property_id parse_nc_property_id(const web::json::value& property_id); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid web::json::value make_nc_class_id(const nc_class_id& class_id); diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 74c671636..d4ff47a85 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -85,19 +85,19 @@ namespace nmos { // Control class models // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/#control-class-models-for-branch-v10-dev - { nmos::details::make_nc_class_id(nc_object_class_id), make_control_class(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), to_vector(make_nc_object_properties()), to_vector(make_nc_object_methods()), to_vector(make_nc_object_events())) }, - { nmos::details::make_nc_class_id(nc_block_class_id), make_control_class(U("NcBlock class descriptor"), nc_block_class_id, U("NcBlock"), to_vector(make_nc_block_properties()), to_vector(make_nc_block_methods()), to_vector(make_nc_block_events())) }, - { nmos::details::make_nc_class_id(nc_worker_class_id), make_control_class(U("NcWorker class descriptor"), nc_worker_class_id, U("NcWorker"), to_vector(make_nc_worker_properties()), to_vector(make_nc_worker_methods()), to_vector(make_nc_worker_events())) }, - { nmos::details::make_nc_class_id(nc_manager_class_id), make_control_class(U("NcManager class descriptor"), nc_manager_class_id, U("NcManager"),to_vector(make_nc_manager_properties()), to_vector(make_nc_manager_methods()), to_vector(make_nc_manager_events())) }, - { nmos::details::make_nc_class_id(nc_device_manager_class_id), make_control_class(U("NcDeviceManager class descriptor"), nc_device_manager_class_id, U("NcDeviceManager"), U("DeviceManager"), to_vector(make_nc_device_manager_properties()), to_vector(make_nc_device_manager_methods()), to_vector(make_nc_device_manager_events())) }, - { nmos::details::make_nc_class_id(nc_class_manager_class_id), make_control_class(U("NcClassManager class descriptor"), nc_class_manager_class_id, U("NcClassManager"), U("ClassManager"), to_vector(make_nc_class_manager_properties()), to_vector(make_nc_class_manager_methods()), to_vector(make_nc_class_manager_events())) }, + { nc_object_class_id, make_control_class(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), to_vector(make_nc_object_properties()), to_vector(make_nc_object_methods()), to_vector(make_nc_object_events())) }, + { nc_block_class_id, make_control_class(U("NcBlock class descriptor"), nc_block_class_id, U("NcBlock"), to_vector(make_nc_block_properties()), to_vector(make_nc_block_methods()), to_vector(make_nc_block_events())) }, + { nc_worker_class_id, make_control_class(U("NcWorker class descriptor"), nc_worker_class_id, U("NcWorker"), to_vector(make_nc_worker_properties()), to_vector(make_nc_worker_methods()), to_vector(make_nc_worker_events())) }, + { nc_manager_class_id, make_control_class(U("NcManager class descriptor"), nc_manager_class_id, U("NcManager"),to_vector(make_nc_manager_properties()), to_vector(make_nc_manager_methods()), to_vector(make_nc_manager_events())) }, + { nc_device_manager_class_id, make_control_class(U("NcDeviceManager class descriptor"), nc_device_manager_class_id, U("NcDeviceManager"), U("DeviceManager"), to_vector(make_nc_device_manager_properties()), to_vector(make_nc_device_manager_methods()), to_vector(make_nc_device_manager_events())) }, + { nc_class_manager_class_id, make_control_class(U("NcClassManager class descriptor"), nc_class_manager_class_id, U("NcClassManager"), U("ClassManager"), to_vector(make_nc_class_manager_properties()), to_vector(make_nc_class_manager_methods()), to_vector(make_nc_class_manager_events())) }, // identification beacon model // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon - { nmos::details::make_nc_class_id(nc_ident_beacon_class_id), make_control_class(U("NcIdentBeacon class descriptor"), nc_ident_beacon_class_id, U("NcIdentBeacon"), to_vector(make_nc_ident_beacon_properties()), to_vector(make_nc_ident_beacon_methods()), to_vector(make_nc_ident_beacon_events())) }, + { nc_ident_beacon_class_id, make_control_class(U("NcIdentBeacon class descriptor"), nc_ident_beacon_class_id, U("NcIdentBeacon"), to_vector(make_nc_ident_beacon_properties()), to_vector(make_nc_ident_beacon_methods()), to_vector(make_nc_ident_beacon_events())) }, // Monitoring // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor - { nmos::details::make_nc_class_id(nc_receiver_monitor_class_id), make_control_class(U("NcReceiverMonitor class descriptor"), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), to_vector(make_nc_receiver_monitor_properties()), to_vector(make_nc_receiver_monitor_methods()), to_vector(make_nc_receiver_monitor_events())) }, - { nmos::details::make_nc_class_id(nc_receiver_monitor_protected_class_id), make_control_class(U("NcReceiverMonitorProtected class descriptor"), nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), to_vector(make_nc_receiver_monitor_protected_properties()), to_vector(make_nc_receiver_monitor_protected_methods()), to_vector(make_nc_receiver_monitor_protected_events())) } + { nc_receiver_monitor_class_id, make_control_class(U("NcReceiverMonitor class descriptor"), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), to_vector(make_nc_receiver_monitor_properties()), to_vector(make_nc_receiver_monitor_methods()), to_vector(make_nc_receiver_monitor_events())) }, + { nc_receiver_monitor_protected_class_id, make_control_class(U("NcReceiverMonitorProtected class descriptor"), nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), to_vector(make_nc_receiver_monitor_protected_properties()), to_vector(make_nc_receiver_monitor_protected_methods()), to_vector(make_nc_receiver_monitor_protected_events())) } }; // setup the core datatypes @@ -173,23 +173,19 @@ namespace nmos // insert control class, false if class already presented bool control_protocol_state::insert(const experimental::control_class& control_class) { - const auto& class_id = nmos::details::make_nc_class_id(control_class.class_id); - auto lock = write_lock(); - if (control_classes.end() == control_classes.find(class_id)) + if (control_classes.end() == control_classes.find(control_class.class_id)) { - control_classes[nmos::details::make_nc_class_id(control_class.class_id)] = control_class; + control_classes[control_class.class_id] = control_class; return true; } return false; } // erase control class of the given class id, false if the required class not found - bool control_protocol_state::erase(nc_class_id class_id_) + bool control_protocol_state::erase(nc_class_id class_id) { - const auto& class_id = nmos::details::make_nc_class_id(class_id_); - auto lock = write_lock(); if (control_classes.end() != control_classes.find(class_id)) diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index ddf68e906..41fd6824a 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -31,13 +31,13 @@ namespace nmos }; // nc_class_id vs control_class - typedef std::map control_classes; + typedef std::map control_classes; // nc_name vs datatype typedef std::map datatypes; // methods defnitions typedef std::function method; - typedef std::map methods; // method_id vs method handler + typedef std::map methods; // method_id vs method handler struct control_protocol_state { diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index 310a16af4..a7d6bc1a1 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -131,6 +131,16 @@ namespace nmos { uint16_t level; uint16_t index; + + nc_element_id(uint16_t level, uint16_t index) + : level(level) + , index(index) + {} + + auto tied() const -> decltype(std::tie(level, index)) { return std::tie(level, index); } + friend bool operator==(const nc_element_id& lhs, const nc_element_id& rhs) { return lhs.tied() == rhs.tied(); } + friend bool operator!=(const nc_element_id& lhs, const nc_element_id& rhs) { return !(lhs == rhs); } + friend bool operator<(const nc_element_id& lhs, const nc_element_id& rhs) { return lhs.tied() < rhs.tied(); } }; // NcEventId diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 9e9c80784..76926f4d6 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -57,7 +57,8 @@ namespace nmos while (!class_id.empty()) { - auto class_found = control_classes.find(nmos::details::make_nc_class_id(class_id)); +// auto class_found = control_classes.find(nmos::details::make_nc_class_id(class_id)); + auto class_found = control_classes.find(class_id); if (control_classes.end() != class_found) { auto& properties = class_found->second.properties.as_array(); @@ -76,7 +77,8 @@ namespace nmos } // hmm, change method_id to struct, and bring in method handlers via the control_classes - nmos::experimental::method find_method(const web::json::value& method_id, const nc_class_id& class_id_, const nmos::experimental::control_classes& control_classes) +// nmos::experimental::method find_method(const web::json::value& method_id, const nc_class_id& class_id_, const nmos::experimental::control_classes& control_classes) + nmos::experimental::method find_method(const nc_method_id& method_id, const nc_class_id& class_id_, const nmos::experimental::control_classes& control_classes) { using web::json::value; using web::json::value_of; @@ -503,7 +505,8 @@ namespace nmos // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - auto class_found = control_classes.find(nmos::details::make_nc_class_id(class_id)); +// auto class_found = control_classes.find(nmos::details::make_nc_class_id(class_id)); + auto class_found = control_classes.find(class_id); if (control_classes.end() != class_found) { @@ -522,7 +525,8 @@ namespace nmos { while (!id.empty()) { - auto found = control_classes.find(nmos::details::make_nc_class_id(id)); +// auto found = control_classes.find(nmos::details::make_nc_class_id(id)); + auto found = control_classes.find(id); if (control_classes.end() != found) { for (const auto& property : found->second.properties.as_array()) { web::json::push_back(properties, property); } @@ -608,6 +612,7 @@ namespace nmos // NcObject methods // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject +/* nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 1 } })] = get; nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 2 } })] = set; nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 3 } })] = get_sequence_item; @@ -615,13 +620,27 @@ namespace nmos nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 5 } })] = add_sequence_item; nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 6 } })] = remove_sequence_item; nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 7 } })] = get_sequence_length; +*/ + nc_object_method_handlers[{1, 1}] = get; + nc_object_method_handlers[{1, 2}] = set; + nc_object_method_handlers[{1, 3}] = get_sequence_item; + nc_object_method_handlers[{1, 4}] = set_sequence_item; + nc_object_method_handlers[{1, 5}] = add_sequence_item; + nc_object_method_handlers[{1, 6}] = remove_sequence_item; + nc_object_method_handlers[{1, 7}] = get_sequence_length; // NcBlock methods // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock +/* nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 1 } })] = get_member_descriptors; nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 2 } })] = find_members_by_path; nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 3 } })] = find_members_by_role; nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 4 } })] = find_members_by_class_id; +*/ + nc_block_method_handlers[{2, 1}] = get_member_descriptors; + nc_block_method_handlers[{2, 2}] = find_members_by_path; + nc_block_method_handlers[{2, 3}] = find_members_by_role; + nc_block_method_handlers[{2, 4}] = find_members_by_class_id; // NcWorker has no extended method // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker @@ -634,29 +653,34 @@ namespace nmos // NcClassManager methods // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager +/* nc_class_manager_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 1 } })] = get_control_class; nc_class_manager_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 2 } })] = get_datatype; +*/ + nc_class_manager_method_handlers[{3, 1}] = get_control_class; + nc_class_manager_method_handlers[{3, 2}] = get_datatype; // class id vs method handlers // hmm, todo, custom class and assoicated methods will need to be inserted within follwoing table! - const std::map methods = + const std::map methods = { - { nmos::details::make_nc_class_id(nc_object_class_id), nc_object_method_handlers }, - { nmos::details::make_nc_class_id(nc_block_class_id), nc_block_method_handlers }, - { nmos::details::make_nc_class_id(nc_class_manager_class_id), nc_class_manager_method_handlers } + { nc_object_class_id, nc_object_method_handlers }, + { nc_block_class_id, nc_block_method_handlers }, + { nc_class_manager_class_id, nc_class_manager_method_handlers } }; + auto class_id = class_id_; while (!class_id.empty()) { - auto subset_methods_found = methods.find(nmos::details::make_nc_class_id(class_id)); + auto class_id_methods_found = methods.find(class_id); - if (methods.end() != subset_methods_found) + if (methods.end() != class_id_methods_found) { - auto& subset_methods = subset_methods_found->second; - auto method_found = subset_methods.find(method_id); - if (subset_methods.end() != method_found) + auto& class_id_methods = class_id_methods_found->second; + auto method_found = class_id_methods.find(method_id); + if (class_id_methods.end() != method_found) { return method_found->second; } @@ -858,7 +882,7 @@ namespace nmos const auto oid = nmos::fields::nc::oid(cmd); // get methodId - const auto& method_id = nmos::fields::nc::method_id(cmd); + const auto& method_id = nmos::details::parse_nc_method_id(nmos::fields::nc::method_id(cmd)); // get arguments const auto& arguments = nmos::fields::nc::arguments(cmd); @@ -878,7 +902,7 @@ namespace nmos else { utility::stringstream_t ss; - ss << U("unsupported method id: ") << method_id.serialize(); + ss << U("unsupported method id: ") << nmos::fields::nc::method_id(cmd).serialize(); web::json::push_back(responses, make_control_protocol_error_response(handle, { nc_method_status::method_not_implemented }, ss.str())); } From 40c3dc134d7711ed1c212f2783361f767a6bf68b Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 30 Aug 2023 10:45:03 +0100 Subject: [PATCH 035/250] Remove un-used code --- Development/nmos/control_protocol_ws_api.cpp | 46 ++++++-------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 76926f4d6..8f22bbc99 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -57,7 +57,6 @@ namespace nmos while (!class_id.empty()) { -// auto class_found = control_classes.find(nmos::details::make_nc_class_id(class_id)); auto class_found = control_classes.find(class_id); if (control_classes.end() != class_found) { @@ -76,8 +75,6 @@ namespace nmos return value::null(); } - // hmm, change method_id to struct, and bring in method handlers via the control_classes -// nmos::experimental::method find_method(const web::json::value& method_id, const nc_class_id& class_id_, const nmos::experimental::control_classes& control_classes) nmos::experimental::method find_method(const nc_method_id& method_id, const nc_class_id& class_id_, const nmos::experimental::control_classes& control_classes) { using web::json::value; @@ -490,7 +487,7 @@ namespace nmos // NcClassManager methods implementation // Get a single class descriptor - const auto get_control_class = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) + const auto get_control_class = [](nmos::resources&, nmos::resources::iterator, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) { const auto& class_id = parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements @@ -505,16 +502,15 @@ namespace nmos // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... -// auto class_found = control_classes.find(nmos::details::make_nc_class_id(class_id)); auto class_found = control_classes.find(class_id); if (control_classes.end() != class_found) { auto id = class_id; - auto description = class_found->second.description; - auto name = class_found->second.name; - auto fixed_role = class_found->second.fixed_role; + auto& description = class_found->second.description; + auto& name = class_found->second.name; + auto& fixed_role = class_found->second.fixed_role; auto properties = class_found->second.properties; auto methods = class_found->second.methods; auto events = class_found->second.events; @@ -525,7 +521,6 @@ namespace nmos { while (!id.empty()) { -// auto found = control_classes.find(nmos::details::make_nc_class_id(id)); auto found = control_classes.find(id); if (control_classes.end() != found) { @@ -544,7 +539,7 @@ namespace nmos return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("classId not found")); }; // Get a single datatype descriptor - const auto get_datatype = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes&, const nmos::experimental::datatypes& datatypes, slog::base_gate& gate) + const auto get_datatype = [](nmos::resources&, nmos::resources::iterator, int32_t handle, const value& arguments, const nmos::experimental::control_classes&, const nmos::experimental::datatypes& datatypes, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -612,15 +607,6 @@ namespace nmos // NcObject methods // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject -/* - nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 1 } })] = get; - nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 2 } })] = set; - nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 3 } })] = get_sequence_item; - nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 4 } })] = set_sequence_item; - nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 5 } })] = add_sequence_item; - nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 6 } })] = remove_sequence_item; - nc_object_method_handlers[value_of({ { nmos::fields::nc::level, 1 }, { nmos::fields::nc::index, 7 } })] = get_sequence_length; -*/ nc_object_method_handlers[{1, 1}] = get; nc_object_method_handlers[{1, 2}] = set; nc_object_method_handlers[{1, 3}] = get_sequence_item; @@ -631,12 +617,6 @@ namespace nmos // NcBlock methods // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock -/* - nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 1 } })] = get_member_descriptors; - nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 2 } })] = find_members_by_path; - nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 3 } })] = find_members_by_role; - nc_block_method_handlers[value_of({ { nmos::fields::nc::level, 2 }, { nmos::fields::nc::index, 4 } })] = find_members_by_class_id; -*/ nc_block_method_handlers[{2, 1}] = get_member_descriptors; nc_block_method_handlers[{2, 2}] = find_members_by_path; nc_block_method_handlers[{2, 3}] = find_members_by_role; @@ -653,10 +633,6 @@ namespace nmos // NcClassManager methods // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager -/* - nc_class_manager_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 1 } })] = get_control_class; - nc_class_manager_method_handlers[value_of({ { nmos::fields::nc::level, 3 }, { nmos::fields::nc::index, 2 } })] = get_datatype; -*/ nc_class_manager_method_handlers[{3, 1}] = get_control_class; nc_class_manager_method_handlers[{3, 2}] = get_datatype; @@ -678,12 +654,16 @@ namespace nmos if (methods.end() != class_id_methods_found) { - auto& class_id_methods = class_id_methods_found->second; - auto method_found = class_id_methods.find(method_id); - if (class_id_methods.end() != method_found) + auto& method_id_methods = class_id_methods_found->second; + auto method_found = method_id_methods.find(method_id); + if (method_id_methods.end() != method_found) { return method_found->second; } + else + { + //control_classes. + } } class_id.pop_back(); } @@ -890,7 +870,7 @@ namespace nmos auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != resource) { - auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); + const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); // find the relevent method handler to execute auto method = details::find_method(method_id, class_id, get_control_protocol_classes()); From e4e54d64e7314556e060ef4929d10e3ec6966207 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 31 Aug 2023 12:16:35 +0100 Subject: [PATCH 036/250] Add support for allowing user to insert non-standard control class method handler --- Development/cmake/NmosCppLibraries.cmake | 2 + Development/nmos-cpp-node/main.cpp | 5 +- .../nmos-cpp-node/node_implementation.cpp | 16 +- .../nmos/control_protocol_handlers.cpp | 46 +- Development/nmos/control_protocol_handlers.h | 38 +- Development/nmos/control_protocol_methods.cpp | 550 ++++++++++++++++ Development/nmos/control_protocol_methods.h | 50 ++ .../nmos/control_protocol_resource.cpp | 40 +- Development/nmos/control_protocol_resource.h | 40 +- Development/nmos/control_protocol_state.cpp | 101 ++- Development/nmos/control_protocol_state.h | 23 +- Development/nmos/control_protocol_typedefs.h | 4 + Development/nmos/control_protocol_utils.cpp | 39 +- Development/nmos/control_protocol_utils.h | 16 +- Development/nmos/control_protocol_ws_api.cpp | 617 +----------------- Development/nmos/control_protocol_ws_api.h | 6 +- Development/nmos/node_server.cpp | 2 +- Development/nmos/node_server.h | 17 +- 18 files changed, 892 insertions(+), 720 deletions(-) create mode 100644 Development/nmos/control_protocol_methods.cpp create mode 100644 Development/nmos/control_protocol_methods.h diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index f29a683c7..1fe469e1a 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -832,6 +832,7 @@ set(NMOS_CPP_NMOS_SOURCES nmos/connection_events_activation.cpp nmos/connection_resources.cpp nmos/control_protocol_handlers.cpp + nmos/control_protocol_methods.cpp nmos/control_protocol_resource.cpp nmos/control_protocol_resources.cpp nmos/control_protocol_state.cpp @@ -911,6 +912,7 @@ set(NMOS_CPP_NMOS_HEADERS nmos/connection_events_activation.h nmos/connection_resources.h nmos/control_protocol_handlers.h + nmos/control_protocol_methods.h nmos/control_protocol_resource.h nmos/control_protocol_resources.h nmos/control_protocol_state.h diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index 5dd65c38e..ee133835c 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -112,8 +112,9 @@ int main(int argc, char* argv[]) nmos::experimental::control_protocol_state control_protocol_state; if (0 <= nmos::fields::control_protocol_ws_port(node_model.settings)) { - node_implementation.on_get_control_classes(nmos::make_get_control_protocol_classes_handler(control_protocol_state, gate)); - node_implementation.on_get_control_datatypes(nmos::make_get_control_protocol_datatypes_handler(control_protocol_state, gate)); + node_implementation.on_get_control_class(nmos::make_get_control_protocol_class_handler(control_protocol_state, gate)); + node_implementation.on_get_control_datatype(nmos::make_get_control_protocol_datatype_handler(control_protocol_state, gate)); + node_implementation.on_get_control_protocol_methods(nmos::make_get_control_protocol_methods_handler(control_protocol_state, gate)); } // Set up the node server diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index d7bbbcc36..aa4ab3324 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -909,8 +909,22 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // example to create a non-standard Gain control class const auto gain_control_class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 1 }); const web::json::field_as_number gain_value{ U("gainValue") }; + // Gain control class properties std::vector gain_control_properties = { nmos::experimental::make_control_class_property(U("Gain value"), { 3, 1 }, gain_value, U("NcFloat32")) }; - auto gain_control_class = nmos::experimental::make_control_class(U("Gain control class descriptor"), gain_control_class_id, U("GainControl"), gain_control_properties, {}, {}); + // Gain control class method example + auto example_method = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + { + slog::log(gate, SLOG_FLF) << "Executing the example method"; + return nmos::make_control_protocol_response(handle, { nmos::nc_method_status::ok }); + }; + // Gain control class methods + std::vector> gain_control_methods = + { + { nmos::experimental::make_control_class_method(U("This is an example method"), {3, 1}, U("ExampleMethod"), U("NcMethodResult"), {}, false), example_method } + }; + // create Gain control class + auto gain_control_class = nmos::experimental::make_control_class(U("Gain control class descriptor"), gain_control_class_id, U("GainControl"), gain_control_properties, gain_control_methods, {}); + // insert Gain control class to global state, which will be used by the control_protocol_ws_message_handler to process incoming ws message control_protocol_state.insert(gain_control_class); // helper function to create Gain control instance auto make_gain_control = [&gain_value, &gain_control_class_id](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, float gain = 0.0, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null()) diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index e60f387cb..73fa4fa8e 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -1,19 +1,26 @@ #include "nmos/control_protocol_handlers.h" #include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_state.h" #include "nmos/slog.h" namespace nmos { - get_control_protocol_classes_handler make_get_control_protocol_classes_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) + get_control_protocol_class_handler make_get_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) { - return [&]() + return [&](const nc_class_id& class_id) { - slog::log(gate, SLOG_FLF) << "Retrieve all control classes from cache"; + slog::log(gate, SLOG_FLF) << "Retrieve control class of class_id: " << nmos::details::make_nc_class_id(class_id).serialize() << " from cache"; auto lock = control_protocol_state.read_lock(); - return control_protocol_state.control_classes; + auto& control_classes = control_protocol_state.control_classes; + auto found = control_classes.find(class_id); + if (control_classes.end() != found) + { + return found->second; + } + return nmos::experimental::control_class{}; }; } @@ -36,15 +43,40 @@ namespace nmos }; } - get_control_protocol_datatypes_handler make_get_control_protocol_datatypes_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) + get_control_protocol_datatype_handler make_get_control_protocol_datatype_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) + { + return [&](const nmos::nc_name& name) + { + slog::log(gate, SLOG_FLF) << "Retrieve datatype of name: " << name << " from cache"; + + auto lock = control_protocol_state.read_lock(); + + auto found = control_protocol_state.datatypes.find(name); + if (control_protocol_state.datatypes.end() != found) + { + return found->second; + } + return nmos::experimental::datatype{}; + }; + } + + get_control_protocol_methods_handler make_get_control_protocol_methods_handler(experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) { return [&]() { - slog::log(gate, SLOG_FLF) << "Retrieve all datatypes from cache"; + slog::log(gate, SLOG_FLF) << "Retrieve all method handlers from cache"; + + std::map methods; auto lock = control_protocol_state.read_lock(); - return control_protocol_state.datatypes; + auto& control_classes = control_protocol_state.control_classes; + + for (const auto& control_class : control_classes) + { + methods[control_class.first] = control_class.second.method_handlers; + } + return methods; }; } } diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index 165b53809..ce4c03b45 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -2,7 +2,9 @@ #define NMOS_CONTROL_PROTOCOL_HANDLERS_H #include -#include "nmos/control_protocol_state.h" +#include +#include "nmos/control_protocol_typedefs.h" +#include "nmos/resources.h" namespace slog { @@ -15,28 +17,44 @@ namespace nmos { struct control_protocol_state; struct control_class; + struct datatype; } - // callback to retrieve all control protocol classes + // callback to retrieve a specific control protocol classe // this callback should not throw exceptions - typedef std::function get_control_protocol_classes_handler; + typedef std::function get_control_protocol_class_handler; // callback to add user control protocol class // this callback should not throw exceptions typedef std::function add_control_protocol_class_handler; - // callback to retrieve all control protocol datatypes + // callback to retrieve a control protocol datatype // this callback should not throw exceptions - typedef std::function get_control_protocol_datatypes_handler; + typedef std::function get_control_protocol_datatype_handler; - // construct callback to retrieve all control protocol classes - get_control_protocol_classes_handler make_get_control_protocol_classes_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); + namespace experimental + { + // method handler defnition + typedef std::function method; + // methods defnition + typedef std::map methods; // method_id vs method handler + } + + // callback to retrieve all the method handlers + // this callback should not throw exceptions + typedef std::function()> get_control_protocol_methods_handler; + + // construct callback to retrieve a specific control protocol class + get_control_protocol_class_handler make_get_control_protocol_class_handler(experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); // construct callback to add control protocol class - add_control_protocol_class_handler make_add_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); + add_control_protocol_class_handler make_add_control_protocol_class_handler(experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); + + // construct callback to retrieve a specific datatype + get_control_protocol_datatype_handler make_get_control_protocol_datatype_handler(experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); - // construct callback to retrieve all datatypes - get_control_protocol_datatypes_handler make_get_control_protocol_datatypes_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); + // construct callback to retrieve all method handlers + get_control_protocol_methods_handler make_get_control_protocol_methods_handler(experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); } #endif diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp new file mode 100644 index 000000000..8aec5aec1 --- /dev/null +++ b/Development/nmos/control_protocol_methods.cpp @@ -0,0 +1,550 @@ +#include "nmos/control_protocol_methods.h" + +#include "cpprest/json_utils.h" +#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_state.h" +#include "nmos/control_protocol_utils.h" +#include "nmos/json_fields.h" +#include "nmos/slog.h" + +namespace nmos +{ + namespace details + { + // NcObject methods implementation + // Get property value + web::json::value get(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + const auto& property_id = nmos::fields::nc::id(arguments); + + slog::log(gate, SLOG_FLF) << "Get property: " << property_id.serialize(); + + // find the relevant nc_property_descriptor + const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); + if (!property.is_null()) + { + return make_control_protocol_response(handle, { nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(property))); + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do Get"); + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + } + + // Set property value + web::json::value set(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& val = nmos::fields::nc::value(arguments); + + slog::log(gate, SLOG_FLF) << "Set property: " << property_id.serialize() << " value: " << val.serialize(); + + // find the relevant nc_property_descriptor + const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); + if (!property.is_null()) + { + if (nmos::fields::nc::is_read_only(property)) + { + return make_control_protocol_response(handle, { nc_method_status::read_only }); + } + + if ((val.is_null() && !nmos::fields::nc::is_nullable(property)) + || (val.is_array() && !nmos::fields::nc::is_sequence(property))) + { + return make_control_protocol_response(handle, { nc_method_status::parameter_error }); + } + + resources.modify(resource, [&](nmos::resource& resource) + { + resource.data[nmos::fields::nc::name(property)] = val; + + resource.updated = strictly_increasing_update(resources); + }); + return make_control_protocol_response(handle, { nc_method_status::ok }); + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << " to do Set"; + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + } + + // Get sequence item + web::json::value get_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& index = nmos::fields::nc::index(arguments); + + slog::log(gate, SLOG_FLF) << "Get sequence item: " << property_id.serialize() << " index: " << index; + + // find the relevant nc_property_descriptor + const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); + if (!property.is_null()) + { + if (!nmos::fields::nc::is_sequence(property)) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); + } + + auto& data = resource->data.at(nmos::fields::nc::name(property)); + + if (!data.is_null() && data.as_array().size() > (size_t)index) + { + return make_control_protocol_response(handle, { nc_method_status::ok }, data.at(index)); + } + + // out of bound + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do GetSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do GetSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + } + + // Set sequence item + web::json::value set_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& index = nmos::fields::nc::index(arguments); + const auto& val = nmos::fields::nc::value(arguments); + + slog::log(gate, SLOG_FLF) << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize(); + + // find the relevant nc_property_descriptor + const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); + if (!property.is_null()) + { + if (!nmos::fields::nc::is_sequence(property)) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do SetSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); + } + + auto& data = resource->data.at(nmos::fields::nc::name(property)); + + if (!data.is_null() && data.as_array().size() > (size_t)index) + { + resources.modify(resource, [&](nmos::resource& resource) + { + resource.data[nmos::fields::nc::name(property)][index] = val; + + resource.updated = strictly_increasing_update(resources); + }); + return make_control_protocol_response(handle, { nc_method_status::ok }); + } + + // out of bound + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do SetSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do SetSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + } + + // Add item to sequence + web::json::value add_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + using web::json::value; + + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& val = nmos::fields::nc::value(arguments); + + slog::log(gate, SLOG_FLF) << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize(); + + // find the relevant nc_property_descriptor + const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); + if (!property.is_null()) + { + if (!nmos::fields::nc::is_sequence(property)) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do AddSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); + } + + auto& data = resource->data.at(nmos::fields::nc::name(property)); + + resources.modify(resource, [&](nmos::resource& resource) + { + auto& sequence = resource.data[nmos::fields::nc::name(property)]; + if (data.is_null()) { sequence = value::array(); } + web::json::push_back(sequence, val); + + resource.updated = strictly_increasing_update(resources); + }); + return make_control_protocol_response(handle, { nc_method_status::ok }, uint32_t(data.as_array().size() - 1)); + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do AddSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + } + + // Delete sequence item + web::json::value remove_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& index = nmos::fields::nc::index(arguments); + + slog::log(gate, SLOG_FLF) << "Remove sequence item: " << property_id.serialize() << " index: " << index; + + // find the relevant nc_property_descriptor + const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); + if (!property.is_null()) + { + if (!nmos::fields::nc::is_sequence(property)) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do RemoveSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); + } + + auto& data = resource->data.at(nmos::fields::nc::name(property)); + + if (!data.is_null() && data.as_array().size() > (size_t)index) + { + resources.modify(resource, [&](nmos::resource& resource) + { + auto& sequence = resource.data[nmos::fields::nc::name(property)].as_array(); + sequence.erase(index); + + resource.updated = strictly_increasing_update(resources); + }); + return make_control_protocol_response(handle, { nc_method_status::ok }); + } + + // out of bound + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do RemoveSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do RemoveSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + } + + // Get sequence length + web::json::value get_sequence_length(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + using web::json::value; + + const auto& property_id = nmos::fields::nc::id(arguments); + + slog::log(gate, SLOG_FLF) << "Get sequence length: " << property_id.serialize(); + + // find the relevant nc_property_descriptor + const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); + if (!property.is_null()) + { + if (!nmos::fields::nc::is_sequence(property)) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceLength"); + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); + } + + auto& data = resource->data.at(nmos::fields::nc::name(property)); + + if (nmos::fields::nc::is_nullable(property)) + { + // can be null + if (data.is_null()) + { + // null + return make_control_protocol_response(handle, { nc_method_status::ok }, value::null()); + } + } + else + { + // cannot be null + if (data.is_null()) + { + // null + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << " is a null sequence to do GetSequenceLength"; + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); + } + } + return make_control_protocol_response(handle, { nc_method_status::ok }, uint32_t(data.as_array().size())); + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << " to do GetSequenceLength"; + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + } + + // NcBlock methods implementation + // Get descriptors of members of the block + web::json::value get_member_descriptors(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + using web::json::value; + + const auto& recurse = nmos::fields::nc::recurse(arguments); // If recurse is set to true, nested members is to be retrieved + + slog::log(gate, SLOG_FLF) << "Get descriptors of members of the block: " << "recurse: " << recurse; + + auto descriptors = value::array(); + nmos::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); + + return make_control_protocol_response(handle, { nc_method_status::ok }, descriptors); + } + + // Finds member(s) by path + web::json::value find_members_by_path(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + using web::json::value; + + // Relative path to search for (MUST not include the role of the block targeted by oid) + const auto& path = arguments.at(nmos::fields::nc::path); + + slog::log(gate, SLOG_FLF) << "Find member(s) by path: " << "path: " << path.serialize(); + + if (0 == path.size()) + { + // empty path + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty path to do FindMembersByPath")); + } + + auto nc_block_member_descriptors = value::array(); + value nc_block_member_descriptor; + + for (const auto& role : path.as_array()) + { + // look for the role in members + if (resource->data.has_field(nmos::fields::nc::members)) + { + auto& members = nmos::fields::nc::members(resource->data); + auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& nc_block_member_descriptor) + { + return role.as_string() == nmos::fields::nc::role(nc_block_member_descriptor); + }); + + if (members.end() != member_found) + { + nc_block_member_descriptor = *member_found; + + // use oid to look for the next resource + resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(*member_found)))); + } + else + { + // no role + utility::stringstream_t ss; + ss << U("role: ") << role.as_string() << U(" not found to do FindMembersByPath"); + return make_control_protocol_error_response(handle, { nc_method_status::bad_oid }, ss.str()); + } + } + else + { + // no members + return make_control_protocol_error_response(handle, { nc_method_status::bad_oid }, U("no members to do FindMembersByPath")); + } + } + + web::json::push_back(nc_block_member_descriptors, nc_block_member_descriptor); + return make_control_protocol_response(handle, { nc_method_status::ok }, nc_block_member_descriptors); + } + + // Finds members with given role name or fragment + web::json::value find_members_by_role(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + using web::json::value; + + const auto& role = nmos::fields::nc::role(arguments); // Role text to search for + const auto& case_sensitive = nmos::fields::nc::case_sensitive(arguments); // Signals if the comparison should be case sensitive + const auto& match_whole_string = nmos::fields::nc::match_whole_string(arguments); // TRUE to only return exact matches + const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks + + slog::log(gate, SLOG_FLF) << "Find members with given role name or fragment: " << "role: " << role; + + if (role.empty()) + { + // empty role + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty role to do FindMembersByRole")); + } + + auto descriptors = value::array(); + nmos::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); + + return make_control_protocol_response(handle, { nc_method_status::ok }, descriptors); + } + + // Finds members with given class id + web::json::value find_members_by_class_id(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + using web::json::value; + + const auto& class_id = parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for + const auto& include_derived = nmos::fields::nc::include_derived(arguments); // If TRUE it will also include derived class descriptors + const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks + + slog::log(gate, SLOG_FLF) << "Find members with given class id: " << "class_id: " << nmos::details::make_nc_class_id(class_id).serialize(); + + if (class_id.empty()) + { + // empty class_id + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty classId to do FindMembersByClassId")); + } + + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + auto descriptors = value::array(); + nmos::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); + + return make_control_protocol_response(handle, { nc_method_status::ok }, descriptors); + } + + // NcClassManager methods implementation + // Get a single class descriptor + web::json::value get_control_class(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + { + const auto& class_id = parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for + const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements + + slog::log(gate, SLOG_FLF) << "Get a single class descriptor: " << "class_id: " << nmos::details::make_nc_class_id(class_id).serialize(); + + if (class_id.empty()) + { + // empty class_id + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty classId to do GetControlClass")); + } + + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + const auto& control_class = get_control_protocol_class(class_id); + if (!control_class.class_id.empty()) + { + auto& description = control_class.description; + auto& name = control_class.name; + auto& fixed_role = control_class.fixed_role; + auto properties = control_class.properties; + auto methods = control_class.methods; + auto events = control_class.events; + + if (include_inherited) + { + auto inherited_class_id = class_id; + inherited_class_id.pop_back(); + + while (!inherited_class_id.empty()) + { + const auto& inherited_control_class = get_control_protocol_class(inherited_class_id); + { + for (const auto& property : inherited_control_class.properties.as_array()) { web::json::push_back(properties, property); } + for (const auto& method : inherited_control_class.methods.as_array()) { web::json::push_back(methods, method); } + for (const auto& event : inherited_control_class.events.as_array()) { web::json::push_back(events, event); } + } + inherited_class_id.pop_back(); + } + } + auto descriptor = details::make_nc_class_descriptor(description, class_id, name, fixed_role, properties, methods, events); + + return make_control_protocol_response(handle, { nc_method_status::ok }, descriptor); + } + + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("classId not found")); + } + + // Get a single datatype descriptor + web::json::value get_datatype(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + const auto& name = nmos::fields::nc::name(arguments); // name of datatype + const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements + + slog::log(gate, SLOG_FLF) << "Get a single datatype descriptor: " << "name: " << name; + + if (name.empty()) + { + // empty name + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty name to do GetDatatype")); + } + + const auto& datatype = get_control_protocol_datatype(name); + if (datatype.descriptor.size()) + { + auto descriptor = datatype.descriptor; + + if (include_inherited) + { + const auto& type = nmos::fields::nc::type(descriptor); + if (nc_datatype_type::Struct == type) + { + auto descriptor_ = descriptor; + + for (;;) + { + const auto& parent_type = descriptor_.at(nmos::fields::nc::parent_type); + if (!parent_type.is_null()) + { + const auto& parent_datatype = get_control_protocol_datatype(parent_type.as_string()); + if (parent_datatype.descriptor.size()) + { + descriptor_ = parent_datatype.descriptor; + + const auto& fields = nmos::fields::nc::fields(descriptor_); + for (const auto& field : fields) + { + web::json::push_back(descriptor.at(nmos::fields::nc::fields), field); + } + } + } + else + { + break; + } + } + } + } + + return make_control_protocol_response(handle, { nc_method_status::ok }, descriptor); + } + + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("name not found")); + } + } +} diff --git a/Development/nmos/control_protocol_methods.h b/Development/nmos/control_protocol_methods.h new file mode 100644 index 000000000..3e7d136e5 --- /dev/null +++ b/Development/nmos/control_protocol_methods.h @@ -0,0 +1,50 @@ +#ifndef NMOS_CONTROL_PROTOCOL_METHODS_H +#define NMOS_CONTROL_PROTOCOL_METHODS_H + +#include "nmos/control_protocol_handlers.h" +#include "nmos/resources.h" + +namespace slog +{ + class base_gate; +} + +namespace nmos +{ + namespace details + { + // NcObject methods implementation + // Get property value + web::json::value get(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + // Set property value + web::json::value set(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + // Get sequence item + web::json::value get_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + // Set sequence item + web::json::value set_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + // Add item to sequence + web::json::value add_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + // Delete sequence item + web::json::value remove_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + // Get sequence length + web::json::value get_sequence_length(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + + // NcBlock methods implementation + // Get descriptors of members of the block + web::json::value get_member_descriptors(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate); + // Finds member(s) by path + web::json::value find_members_by_path(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate); + // Finds members with given role name or fragment + web::json::value find_members_by_role(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate); + // Finds members with given class id + web::json::value find_members_by_class_id(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate); + + // NcClassManager methods implementation + // Get a single class descriptor + web::json::value get_control_class(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + // Get a single datatype descriptor + web::json::value get_datatype(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate); + } +} + +#endif diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 12d8da0a8..5cc1ec3eb 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -179,7 +179,7 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassdescriptor // description can be null // fixedRole can be null - web::json::value make_nc_class_descriptor(const web::json::value& description, const nc_class_id& class_id, const utility::string_t& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) + web::json::value make_nc_class_descriptor(const web::json::value& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) { using web::json::value; @@ -193,7 +193,7 @@ namespace nmos return data; } - web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) + web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) { using web::json::value; @@ -202,7 +202,7 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncenumitemdescriptor // description can be null - web::json::value make_nc_enum_item_descriptor(const web::json::value& description, const utility::string_t& name, uint16_t val) + web::json::value make_nc_enum_item_descriptor(const web::json::value& description, const nc_name& name, uint16_t val) { using web::json::value; @@ -212,7 +212,7 @@ namespace nmos return data; } - web::json::value make_nc_enum_item_descriptor(const utility::string_t& description, const utility::string_t& name, uint16_t val) + web::json::value make_nc_enum_item_descriptor(const utility::string_t& description, const nc_name& name, uint16_t val) { using web::json::value; @@ -222,7 +222,7 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventdescriptor // description can be null // id = make_nc_event_id(level, index) - web::json::value make_nc_event_descriptor(const web::json::value& description, const nc_event_id& id, const utility::string_t& name, const utility::string_t& event_datatype, bool is_deprecated) + web::json::value make_nc_event_descriptor(const web::json::value& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated) { using web::json::value; @@ -234,7 +234,7 @@ namespace nmos return data; } - web::json::value make_nc_event_descriptor(const utility::string_t& description, const nc_event_id& id, const utility::string_t& name, const utility::string_t& event_datatype, bool is_deprecated) + web::json::value make_nc_event_descriptor(const utility::string_t& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated) { using web::json::value; @@ -245,7 +245,7 @@ namespace nmos // description can be null // type_name can be null // constraints can be null - web::json::value make_nc_field_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + web::json::value make_nc_field_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { using web::json::value; @@ -258,7 +258,7 @@ namespace nmos return data; } - web::json::value make_nc_field_descriptor(const utility::string_t& description, const utility::string_t& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { using web::json::value; @@ -269,7 +269,7 @@ namespace nmos // description can be null // id = make_nc_method_id(level, index) // sequence parameters - web::json::value make_nc_method_descriptor(const web::json::value& description, const nc_method_id& id, const utility::string_t& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) + web::json::value make_nc_method_descriptor(const web::json::value& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) { using web::json::value; @@ -282,7 +282,7 @@ namespace nmos return data; } - web::json::value make_nc_method_descriptor(const utility::string_t& description, const nc_method_id& id, const utility::string_t& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) + web::json::value make_nc_method_descriptor(const utility::string_t& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) { using web::json::value; @@ -292,7 +292,7 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncparameterdescriptor // description can be null // type_name can be null - web::json::value make_nc_parameter_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + web::json::value make_nc_parameter_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { using web::json::value; @@ -305,13 +305,13 @@ namespace nmos return data; } - web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const utility::string_t& name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { using web::json::value; return make_nc_parameter_descriptor(value::string(description), name, value::null(), is_nullable, is_sequence, constraints); } - web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const utility::string_t& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { using web::json::value; @@ -321,7 +321,7 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertydescriptor // description can be null // constraints can be null - web::json::value make_nc_property_descriptor(const web::json::value& description, const nc_property_id& id, const utility::string_t& name, const web::json::value& type_name, + web::json::value make_nc_property_descriptor(const web::json::value& description, const nc_property_id& id, const nc_name& name, const web::json::value& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) { using web::json::value; @@ -338,7 +338,7 @@ namespace nmos return data; } - web::json::value make_nc_property_descriptor(const utility::string_t& description, const nc_property_id& id, const utility::string_t& name, const utility::string_t& type_name, + web::json::value make_nc_property_descriptor(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) { using web::json::value; @@ -349,7 +349,7 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptor // description can be null // constraints can be null - web::json::value make_nc_datatype_descriptor(const web::json::value& description, const utility::string_t& name, nc_datatype_type::type type, const web::json::value& constraints) + web::json::value make_nc_datatype_descriptor(const web::json::value& description, const nc_name& name, nc_datatype_type::type type, const web::json::value& constraints) { using web::json::value; @@ -365,7 +365,7 @@ namespace nmos // description can be null // constraints can be null // items: sequence - web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const utility::string_t& name, const web::json::value& items, const web::json::value& constraints) + web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints) { auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Enum, constraints); data[nmos::fields::nc::items] = items; @@ -376,7 +376,7 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorprimitive // description can be null // constraints can be null - web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints) + web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const nc_name& name, const web::json::value& constraints) { return make_nc_datatype_descriptor(description, name, nc_datatype_type::Primitive, constraints); } @@ -386,7 +386,7 @@ namespace nmos // constraints can be null // fields: sequence // parent_type can be null - web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const utility::string_t& name, const web::json::value& fields, const web::json::value& parent_type, const web::json::value& constraints) + web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const nc_name& name, const web::json::value& fields, const web::json::value& parent_type, const web::json::value& constraints) { auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Struct, constraints); data[nmos::fields::nc::fields] = fields; @@ -398,7 +398,7 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptortypedef // description can be null // constraints can be null - web::json::value make_nc_datatype_typedef(const web::json::value& description, const utility::string_t& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints) + web::json::value make_nc_datatype_typedef(const web::json::value& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints) { using web::json::value; diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 684a0eb44..648b654be 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -70,78 +70,78 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassdescriptor // description can be null // fixedRole can be null - web::json::value make_nc_class_descriptor(const web::json::value& description, const nc_class_id& class_id, const utility::string_t& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); - web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); + web::json::value make_nc_class_descriptor(const web::json::value& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); + web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncenumitemdescriptor // description can be null - web::json::value make_nc_enum_item_descriptor(const web::json::value& description, const utility::string_t& name, uint16_t val); - web::json::value make_nc_enum_item_descriptor(const utility::string_t& description, const utility::string_t& name, uint16_t val); + web::json::value make_nc_enum_item_descriptor(const web::json::value& description, const nc_name& name, uint16_t val); + web::json::value make_nc_enum_item_descriptor(const utility::string_t& description, const nc_name& name, uint16_t val); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventdescriptor // description can be null // id = make_nc_event_id(level, index) - web::json::value make_nc_event_descriptor(const web::json::value& description, const nc_event_id& id, const utility::string_t& name, const utility::string_t& event_datatype, bool is_deprecated); - web::json::value make_nc_event_descriptor(const utility::string_t& description, const nc_event_id& id, const utility::string_t& name, const utility::string_t& event_datatype, bool is_deprecated); + web::json::value make_nc_event_descriptor(const web::json::value& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated); + web::json::value make_nc_event_descriptor(const utility::string_t& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncfielddescriptor // description can be null // type_name can be null // constraints can be null - web::json::value make_nc_field_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); - web::json::value make_nc_field_descriptor(const utility::string_t& description, const utility::string_t& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_field_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethoddescriptor // description can be null // id = make_nc_method_id(level, index) // sequence parameters - web::json::value make_nc_method_descriptor(const web::json::value& description, const nc_method_id& id, const utility::string_t& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated); - web::json::value make_nc_method_descriptor(const utility::string_t& description, const nc_method_id& id, const utility::string_t& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated); + web::json::value make_nc_method_descriptor(const web::json::value& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated); + web::json::value make_nc_method_descriptor(const utility::string_t& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncparameterdescriptor // description can be null // type_name can be null - web::json::value make_nc_parameter_descriptor(const web::json::value& description, const utility::string_t& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); - web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const utility::string_t& name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); - web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const utility::string_t& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_parameter_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertydescriptor // description can be null // id = make_nc_property_id(level, index); // type_name can be null // constraints can be null - web::json::value make_nc_property_descriptor(const web::json::value& description, const nc_property_id& id, const utility::string_t& name, const web::json::value& type_name, + web::json::value make_nc_property_descriptor(const web::json::value& description, const nc_property_id& id, const nc_name& name, const web::json::value& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints = web::json::value::null()); - web::json::value make_nc_property_descriptor(const utility::string_t& description, const nc_property_id& id, const utility::string_t& name, const utility::string_t& type_name, + web::json::value make_nc_property_descriptor(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptor // description can be null // constraints can be null - web::json::value make_nc_datatype_descriptor(const web::json::value& description, const utility::string_t& name, nc_datatype_type::type type, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_datatype_descriptor(const web::json::value& description, const nc_name& name, nc_datatype_type::type type, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorenum // description can be null // constraints can be null // items: sequence - web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const utility::string_t& name, const web::json::value& items, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorprimitive // description can be null // constraints can be null - web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const utility::string_t& name, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const nc_name& name, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorstruct // description can be null // constraints can be null // fields: sequence // parent_type can be null - web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const utility::string_t& name, const web::json::value& fields, const web::json::value& parent_type, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const nc_name& name, const web::json::value& fields, const web::json::value& parent_type, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptortypedef // description can be null // constraints can be null - web::json::value make_nc_datatype_typedef(const web::json::value& description, const utility::string_t& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_datatype_typedef(const web::json::value& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints = web::json::value::null()); // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index d4ff47a85..7376a7ed0 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -1,49 +1,67 @@ #include "nmos/control_protocol_state.h" -#include "nmos/control_protocol_resource.h" // for nc_object_class_id, nc_block_class_id, nc_worker_class_id, nc_manager_class_id, nc_device_manager_class_id, nc_class_manager_class_id definitions +#include "nmos/control_protocol_methods.h" +#include "nmos/control_protocol_resource.h" namespace nmos { namespace experimental { - // create control class property - web::json::value make_control_class_property(const utility::string_t& description, const nc_property_id& id, const utility::string_t& name, const utility::string_t& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) - { - return nmos::details::make_nc_property_descriptor(description, id, name, type_name, is_read_only, is_nullable, is_sequence, is_deprecated, constraints); - } - namespace details { - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const web::json::value& fixed_role, const std::vector& properties_, const std::vector& methods_, const std::vector& events_) + // create control class + // where + // properties: vector of NcPropertyDescriptor which can be constructed using make_control_class_property + // methods: vector of NcMethodDescriptor which can be constructed using make_nc_method_descriptor and the assoicated method handler + // events: vector of NcEventDescriptor can be constructed using make_nc_event_descriptor + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const web::json::value& fixed_role, const std::vector& properties_, const std::vector>& methods_, const std::vector& events_) { using web::json::value; web::json::value properties = value::array(); for (const auto& property : properties_) { web::json::push_back(properties, property); } web::json::value methods = value::array(); - for (const auto& method : methods_) { web::json::push_back(methods, method); } + nmos::experimental::methods method_handlers; + for (const auto& method : methods_) + { + web::json::push_back(methods, method.first); + method_handlers[nmos::details::parse_nc_method_id(nmos::fields::nc::id(method.first))] = method.second; + } web::json::value events = value::array(); for (const auto& event : events_) { web::json::push_back(events, event); } - return { value::string(description), class_id, name, fixed_role, properties, methods, events }; + return { value::string(description), class_id, name, fixed_role, properties, methods, events, method_handlers }; } } - // create control class with fixed role - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const utility::string_t& fixed_role, const std::vector& properties, const std::vector& methods, const std::vector& events) + // where + // properties: vector of NcPropertyDescriptor which can be constructed using make_control_class_property + // methods: vector of NcMethodDescriptor which can be constructed using make_nc_method_descriptor and the assoicated method handler + // events: vector of NcEventDescriptor can be constructed using make_nc_event_descriptor + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const utility::string_t& fixed_role, const std::vector& properties, const std::vector>& methods, const std::vector& events) { using web::json::value; return details::make_control_class(description, class_id, name, value::string(fixed_role), properties, methods, events); } // create control class with no fixed role - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const std::vector& properties, const std::vector& methods, const std::vector& events) + // where + // properties: vector of NcPropertyDescriptor which can be constructed using make_control_class_property + // methods: vector of NcMethodDescriptor which can be constructed using make_nc_method_descriptor and the assoicated method handler + // events: vector of NcEventDescriptor can be constructed using make_nc_event_descriptor + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const std::vector& properties, const std::vector>& methods, const std::vector& events) { using web::json::value; return details::make_control_class(description, class_id, name, value::null(), properties, methods, events); } + // create control class property + web::json::value make_control_class_property(const utility::string_t& description, const nc_property_id& id, const utility::string_t& name, const utility::string_t& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) + { + return nmos::details::make_nc_property_descriptor(description, id, name, type_name, is_read_only, is_nullable, is_sequence, is_deprecated, constraints); + } + // create control class method parameter web::json::value make_control_class_method_parameter(const utility::string_t& description, const utility::string_t& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { @@ -80,24 +98,63 @@ namespace nmos return std::vector{}; }; + auto to_methods_vector = [](const web::json::value& method_data_array, const nmos::experimental::methods& method_handlers) + { + std::vector> methods; + + if (!method_data_array.is_null()) + { + for (auto& method_data : method_data_array.as_array()) + { + methods.push_back({ method_data, method_handlers.at(nmos::details::parse_nc_method_id(nmos::fields::nc::id(method_data))) }); + } + } + return methods; + }; + // setup the core control classes control_classes = { // Control class models // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/#control-class-models-for-branch-v10-dev - { nc_object_class_id, make_control_class(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), to_vector(make_nc_object_properties()), to_vector(make_nc_object_methods()), to_vector(make_nc_object_events())) }, - { nc_block_class_id, make_control_class(U("NcBlock class descriptor"), nc_block_class_id, U("NcBlock"), to_vector(make_nc_block_properties()), to_vector(make_nc_block_methods()), to_vector(make_nc_block_events())) }, - { nc_worker_class_id, make_control_class(U("NcWorker class descriptor"), nc_worker_class_id, U("NcWorker"), to_vector(make_nc_worker_properties()), to_vector(make_nc_worker_methods()), to_vector(make_nc_worker_events())) }, - { nc_manager_class_id, make_control_class(U("NcManager class descriptor"), nc_manager_class_id, U("NcManager"),to_vector(make_nc_manager_properties()), to_vector(make_nc_manager_methods()), to_vector(make_nc_manager_events())) }, - { nc_device_manager_class_id, make_control_class(U("NcDeviceManager class descriptor"), nc_device_manager_class_id, U("NcDeviceManager"), U("DeviceManager"), to_vector(make_nc_device_manager_properties()), to_vector(make_nc_device_manager_methods()), to_vector(make_nc_device_manager_events())) }, - { nc_class_manager_class_id, make_control_class(U("NcClassManager class descriptor"), nc_class_manager_class_id, U("NcClassManager"), U("ClassManager"), to_vector(make_nc_class_manager_properties()), to_vector(make_nc_class_manager_methods()), to_vector(make_nc_class_manager_events())) }, + { nc_object_class_id, make_control_class(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), to_vector(make_nc_object_properties()), + to_methods_vector(make_nc_object_methods(), + { + { {1, 1}, nmos::details::get }, + { {1, 2}, nmos::details::set }, + { {1, 3}, nmos::details::get_sequence_item }, + { {1, 4}, nmos::details::set_sequence_item }, + { {1, 5}, nmos::details::add_sequence_item }, + { {1, 6}, nmos::details::remove_sequence_item }, + { {1, 7}, nmos::details::get_sequence_length } + }), + to_vector(make_nc_object_events())) }, + { nc_block_class_id, make_control_class(U("NcBlock class descriptor"), nc_block_class_id, U("NcBlock"), to_vector(make_nc_block_properties()), + to_methods_vector(make_nc_block_methods(), + { + { {2, 1}, nmos::details::get_member_descriptors }, + { {2, 2}, nmos::details::find_members_by_path }, + { {2, 3}, nmos::details::find_members_by_role }, + { {2, 4}, nmos::details::find_members_by_class_id } + }), + to_vector(make_nc_block_events())) }, + { nc_worker_class_id, make_control_class(U("NcWorker class descriptor"), nc_worker_class_id, U("NcWorker"), to_vector(make_nc_worker_properties()), to_methods_vector(make_nc_worker_methods(), {}), to_vector(make_nc_worker_events())) }, + { nc_manager_class_id, make_control_class(U("NcManager class descriptor"), nc_manager_class_id, U("NcManager"),to_vector(make_nc_manager_properties()), to_methods_vector(make_nc_manager_methods(), {}), to_vector(make_nc_manager_events())) }, + { nc_device_manager_class_id, make_control_class(U("NcDeviceManager class descriptor"), nc_device_manager_class_id, U("NcDeviceManager"), U("DeviceManager"), to_vector(make_nc_device_manager_properties()), to_methods_vector(make_nc_device_manager_methods(), {}), to_vector(make_nc_device_manager_events())) }, + { nc_class_manager_class_id, make_control_class(U("NcClassManager class descriptor"), nc_class_manager_class_id, U("NcClassManager"), U("ClassManager"), to_vector(make_nc_class_manager_properties()), + to_methods_vector(make_nc_class_manager_methods(), + { + { {3, 1}, nmos::details::get_control_class }, + { {3, 2}, nmos::details::get_datatype } + }), + to_vector(make_nc_class_manager_events())) }, // identification beacon model // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon - { nc_ident_beacon_class_id, make_control_class(U("NcIdentBeacon class descriptor"), nc_ident_beacon_class_id, U("NcIdentBeacon"), to_vector(make_nc_ident_beacon_properties()), to_vector(make_nc_ident_beacon_methods()), to_vector(make_nc_ident_beacon_events())) }, + { nc_ident_beacon_class_id, make_control_class(U("NcIdentBeacon class descriptor"), nc_ident_beacon_class_id, U("NcIdentBeacon"), to_vector(make_nc_ident_beacon_properties()), to_methods_vector(make_nc_ident_beacon_methods(), {}), to_vector(make_nc_ident_beacon_events())) }, // Monitoring // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor - { nc_receiver_monitor_class_id, make_control_class(U("NcReceiverMonitor class descriptor"), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), to_vector(make_nc_receiver_monitor_properties()), to_vector(make_nc_receiver_monitor_methods()), to_vector(make_nc_receiver_monitor_events())) }, - { nc_receiver_monitor_protected_class_id, make_control_class(U("NcReceiverMonitorProtected class descriptor"), nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), to_vector(make_nc_receiver_monitor_protected_properties()), to_vector(make_nc_receiver_monitor_protected_methods()), to_vector(make_nc_receiver_monitor_protected_events())) } + { nc_receiver_monitor_class_id, make_control_class(U("NcReceiverMonitor class descriptor"), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), to_vector(make_nc_receiver_monitor_properties()), to_methods_vector(make_nc_receiver_monitor_methods(), {}), to_vector(make_nc_receiver_monitor_events())) }, + { nc_receiver_monitor_protected_class_id, make_control_class(U("NcReceiverMonitorProtected class descriptor"), nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), to_vector(make_nc_receiver_monitor_protected_properties()), to_methods_vector(make_nc_receiver_monitor_protected_methods(), {}), to_vector(make_nc_receiver_monitor_protected_events())) } }; // setup the core datatypes diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index 41fd6824a..77b2571d7 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -3,9 +3,9 @@ #include #include "cpprest/json_utils.h" +#include "nmos/control_protocol_handlers.h" #include "nmos/control_protocol_typedefs.h" #include "nmos/mutex.h" -#include "nmos/resources.h" namespace slog { class base_gate; } @@ -20,9 +20,11 @@ namespace nmos utility::string_t name; web::json::value fixed_role; - web::json::value properties; // array of nc_property_descriptor - web::json::value methods; // array of nc_method_descriptor - web::json::value events; // array of nc_event_descriptor + web::json::value properties = web::json::value::array(); // array of NcPropertyDescriptor + web::json::value methods = web::json::value::array(); // array of NcMethodDescriptor + web::json::value events = web::json::value::array(); // array of NcEventDescriptor + + nmos::experimental::methods method_handlers; // map of method handlers which are associated to this control_class (class_id), but not including its base class }; struct datatype // NcDatatypeDescriptorEnum/NcDatatypeDescriptorPrimitive/NcDatatypeDescriptorStruct/NcDatatypeDescriptorTypeDef @@ -30,14 +32,8 @@ namespace nmos web::json::value descriptor; }; - // nc_class_id vs control_class typedef std::map control_classes; - // nc_name vs datatype - typedef std::map datatypes; - - // methods defnitions - typedef std::function method; - typedef std::map methods; // method_id vs method handler + typedef std::map datatypes; struct control_protocol_state { @@ -79,10 +75,11 @@ namespace nmos // create control class property web::json::value make_control_class_property(const utility::string_t& description, const nc_property_id& id, const utility::string_t& name, const utility::string_t& type_name, bool is_read_only = false, bool is_nullable = false, bool is_sequence = false, bool is_deprecated = false, const web::json::value& constraints = web::json::value::null()); + // create control class with fixed role - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const utility::string_t& fixed_role, const std::vector& properties, const std::vector& methods, const std::vector& events); + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const utility::string_t& fixed_role, const std::vector& properties, const std::vector>& methods, const std::vector& events); // create control class with no fixed role - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const std::vector& properties, const std::vector& methods, const std::vector& events); + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const std::vector& properties, const std::vector>& methods, const std::vector& events); } } diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index a7d6bc1a1..bf33a331a 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -159,6 +159,10 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncid typedef uint32_t nc_id; + // NcName + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncname + typedef utility::string_t nc_name; + // NcOid // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncoid typedef uint32_t nc_oid; diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 3db423168..0bc6a9f24 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -5,7 +5,7 @@ #include #include "cpprest/json_utils.h" #include "nmos/control_protocol_resource.h" -#include "nmos/control_protocol_typedefs.h" +#include "nmos/control_protocol_state.h" #include "nmos/json_fields.h" #include "nmos/resources.h" @@ -25,27 +25,59 @@ namespace nmos } } + // is the given class_id a NcBlock bool is_nc_block(const nc_class_id& class_id) { return details::is_control_class(nc_object_class_id, class_id); } + // is the given class_id a NcManager bool is_nc_manager(const nc_class_id& class_id) { return details::is_control_class(nc_manager_class_id, class_id); } + // is the given class_id a NcDeviceManager bool is_nc_device_manager(const nc_class_id& class_id) { return details::is_control_class(nc_device_manager_class_id, class_id); } + // is the given class_id a NcClassManager bool is_nc_class_manager(const nc_class_id& class_id) { return details::is_control_class(nc_class_manager_class_id, class_id); } - nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const nc_class_id& suffix) + // find control class property (NcPropertyDescriptor) + web::json::value find_property(const nc_property_id& property_id, const nc_class_id& class_id_, get_control_protocol_class_handler get_control_protocol_class) + { + using web::json::value; + + auto class_id = class_id_; + + while (!class_id.empty()) + { + const auto& control_class = get_control_protocol_class(class_id); + auto& properties = control_class.properties.as_array(); + if (properties.size()) + { + for (const auto& property : properties) + { + if (property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property))) + { + return property; + } + } + } + class_id.pop_back(); + } + + return value::null(); + } + + // construct NcClassId + nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const std::vector& suffix) { nc_class_id class_id = prefix; class_id.push_back(authority_key); @@ -53,6 +85,7 @@ namespace nmos return class_id; } + // get descriptors of members of the block void get_member_descriptors(const resources& resources, resources::iterator resource, bool recurse, web::json::array& descriptors) { if (resource->data.has_field(nmos::fields::nc::members)) @@ -82,6 +115,7 @@ namespace nmos } } + // find members with given role name or fragment void find_members_by_role(const resources& resources, resources::iterator resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& descriptors) { auto find_members_by_matching_role = [&](const web::json::array& members) @@ -132,6 +166,7 @@ namespace nmos } } + // find members with given class id void find_members_by_class_id(const resources& resources, resources::iterator resource, const nc_class_id& class_id_, bool include_derived, bool recurse, web::json::array& descriptors) { auto find_members_by_matching_class_id = [&](const web::json::array& members) diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index acb4a7c76..47838ddd6 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -2,25 +2,35 @@ #define NMOS_CONTROL_PROTOCOL_UTILS_H #include "cpprest/basic_utils.h" -#include "nmos/control_protocol_typedefs.h" // for nc_class_id definition -#include "nmos/resources.h" +#include "nmos/control_protocol_handlers.h" namespace nmos { + // is the given class_id a NcBlock bool is_nc_block(const nc_class_id& class_id); + // is the given class_id a NcManager bool is_nc_manager(const nc_class_id& class_id); + // is the given class_id a NcDeviceManager bool is_nc_device_manager(const nc_class_id& class_id); + // is the given class_id a NcClassManager bool is_nc_class_manager(const nc_class_id& class_id); - nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const nc_class_id& suffix); + // find control class property (NcPropertyDescriptor) + web::json::value find_property(const nc_property_id& property_id, const nc_class_id& class_id, get_control_protocol_class_handler get_control_protocol_class); + // construct NcClassId + nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const std::vector& suffix); + + // get descriptors of members of the block void get_member_descriptors(const resources& resources, resources::iterator resource, bool recurse, web::json::array& descriptors); + // find members with given role name or fragment void find_members_by_role(const resources& resources, resources::iterator resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& nc_block_member_descriptors); + // find members with given class id void find_members_by_class_id(const resources& resources, resources::iterator resource, const nc_class_id& class_id, bool include_derived, bool recurse, web::json::array& descriptors); } diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 8f22bbc99..bca663ee2 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -2,12 +2,9 @@ #include #include "cpprest/json_validator.h" -#include "cpprest/regex_utils.h" #include "nmos/api_utils.h" #include "nmos/control_protocol_resources.h" #include "nmos/control_protocol_resource.h" -#include "nmos/control_protocol_state.h" -#include "nmos/control_protocol_utils.h" #include "nmos/is12_versions.h" #include "nmos/json_schema.h" #include "nmos/model.h" @@ -48,604 +45,8 @@ namespace nmos controlprotocol_validator().validate(request_data, experimental::make_controlprotocolapi_subscription_message_schema_uri(version)); } - // hmm, change property to struct - web::json::value find_property(const web::json::value& property_id, const nc_class_id& class_id_, const nmos::experimental::control_classes& control_classes) + nmos::experimental::method find_method(const nc_method_id& method_id, const nc_class_id& class_id_, const std::map& methods) { - using web::json::value; - - auto class_id = class_id_; - - while (!class_id.empty()) - { - auto class_found = control_classes.find(class_id); - if (control_classes.end() != class_found) - { - auto& properties = class_found->second.properties.as_array(); - for (const auto& property : properties) - { - if (property_id == nmos::fields::nc::id(property)) - { - return property; - } - } - } - class_id.pop_back(); - } - - return value::null(); - } - - nmos::experimental::method find_method(const nc_method_id& method_id, const nc_class_id& class_id_, const nmos::experimental::control_classes& control_classes) - { - using web::json::value; - using web::json::value_of; - - // NcObject methods implementation - // Get property value - const auto get = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - const auto& property_id = nmos::fields::nc::id(arguments); - - slog::log(gate, SLOG_FLF) << "Get property: " << property_id.serialize(); - - // find the relevant nc_property_descriptor - const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); - if (!property.is_null()) - { - return make_control_protocol_response(handle, { nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(property))); - } - - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do Get"); - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); - }; - // Set property value - const auto set = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& val = nmos::fields::nc::value(arguments); - - slog::log(gate, SLOG_FLF) << "Set property: " << property_id.serialize() << " value: " << val.serialize(); - - // find the relevant nc_property_descriptor - const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); - if (!property.is_null()) - { - if (nmos::fields::nc::is_read_only(property)) - { - return make_control_protocol_response(handle, { nc_method_status::read_only }); - } - - if ((val.is_null() && !nmos::fields::nc::is_nullable(property)) - || (val.is_array() && !nmos::fields::nc::is_sequence(property))) - { - return make_control_protocol_response(handle, { nc_method_status::parameter_error }); - } - - resources.modify(resource, [&](nmos::resource& resource) - { - resource.data[nmos::fields::nc::name(property)] = val; - - resource.updated = strictly_increasing_update(resources); - }); - return make_control_protocol_response(handle, { nc_method_status::ok }); - } - - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << " to do Set"; - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); - }; - // Get sequence item - const auto get_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& index = nmos::fields::nc::index(arguments); - - slog::log(gate, SLOG_FLF) << "Get sequence item: " << property_id.serialize() << " index: " << index; - - // find the relevant nc_property_descriptor - const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); - if (!property.is_null()) - { - if (!nmos::fields::nc::is_sequence(property)) - { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); - } - - auto& data = resource->data.at(nmos::fields::nc::name(property)); - - if (!data.is_null() && data.as_array().size() > (size_t)index) - { - return make_control_protocol_response(handle, { nc_method_status::ok }, data.at(index)); - } - - // out of bound - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do GetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); - } - - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do GetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); - }; - // Set sequence item - const auto set_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& index = nmos::fields::nc::index(arguments); - const auto& val = nmos::fields::nc::value(arguments); - - slog::log(gate, SLOG_FLF) << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize(); - - // find the relevant nc_property_descriptor - const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); - if (!property.is_null()) - { - if (!nmos::fields::nc::is_sequence(property)) - { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do SetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); - } - - auto& data = resource->data.at(nmos::fields::nc::name(property)); - - if (!data.is_null() && data.as_array().size() > (size_t)index) - { - resources.modify(resource, [&](nmos::resource& resource) - { - resource.data[nmos::fields::nc::name(property)][index] = val; - - resource.updated = strictly_increasing_update(resources); - }); - return make_control_protocol_response(handle, { nc_method_status::ok }); - } - - // out of bound - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do SetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); - } - - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do SetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); - }; - // Add item to sequence - const auto add_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& val = nmos::fields::nc::value(arguments); - - slog::log(gate, SLOG_FLF) << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize(); - - // find the relevant nc_property_descriptor - const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); - if (!property.is_null()) - { - if (!nmos::fields::nc::is_sequence(property)) - { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do AddSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); - } - - auto& data = resource->data.at(nmos::fields::nc::name(property)); - - resources.modify(resource, [&](nmos::resource& resource) - { - auto& sequence = resource.data[nmos::fields::nc::name(property)]; - if (data.is_null()) { sequence = value::array(); } - web::json::push_back(sequence, val); - - resource.updated = strictly_increasing_update(resources); - }); - return make_control_protocol_response(handle, { nc_method_status::ok }, uint32_t(data.as_array().size() - 1)); - } - - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do AddSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); - }; - // Delete sequence item - const auto remove_sequence_item = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& index = nmos::fields::nc::index(arguments); - - slog::log(gate, SLOG_FLF) << "Remove sequence item: " << property_id.serialize() << " index: " << index; - - // find the relevant nc_property_descriptor - const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); - if (!property.is_null()) - { - if (!nmos::fields::nc::is_sequence(property)) - { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do RemoveSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); - } - - auto& data = resource->data.at(nmos::fields::nc::name(property)); - - if (!data.is_null() && data.as_array().size() > (size_t)index) - { - resources.modify(resource, [&](nmos::resource& resource) - { - auto& sequence = resource.data[nmos::fields::nc::name(property)].as_array(); - sequence.erase(index); - - resource.updated = strictly_increasing_update(resources); - }); - return make_control_protocol_response(handle, { nc_method_status::ok }); - } - - // out of bound - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do RemoveSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); - } - - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do RemoveSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); - }; - // Get sequence length - const auto get_sequence_length = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - const auto& property_id = nmos::fields::nc::id(arguments); - - slog::log(gate, SLOG_FLF) << "Get sequence length: " << property_id.serialize(); - - // find the relevant nc_property_descriptor - const auto& property = find_property(property_id, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), control_classes); - if (!property.is_null()) - { - if (!nmos::fields::nc::is_sequence(property)) - { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceLength"); - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); - } - - auto& data = resource->data.at(nmos::fields::nc::name(property)); - - if (nmos::fields::nc::is_nullable(property)) - { - // can be null - if (data.is_null()) - { - // null - return make_control_protocol_response(handle, { nc_method_status::ok }, value::null()); - } - } - else - { - // cannot be null - if (data.is_null()) - { - // null - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << " is a null sequence to do GetSequenceLength"; - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); - } - } - return make_control_protocol_response(handle, { nc_method_status::ok }, uint32_t(data.as_array().size())); - } - - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << " to do GetSequenceLength"; - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); - }; - - // NcBlock methods implementation - // Get descriptors of members of the block - const auto get_member_descriptors = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes&, const nmos::experimental::datatypes&, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - const auto& recurse = nmos::fields::nc::recurse(arguments); // If recurse is set to true, nested members is to be retrieved - - slog::log(gate, SLOG_FLF) << "Get descriptors of members of the block: " << "recurse: " << recurse; - - auto descriptors = value::array(); - nmos::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); - - return make_control_protocol_response(handle, { nc_method_status::ok }, descriptors); - }; - // Finds member(s) by path - const auto find_members_by_path = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes&, const nmos::experimental::datatypes&, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - // Relative path to search for (MUST not include the role of the block targeted by oid) - const auto& path = arguments.at(nmos::fields::nc::path); - - slog::log(gate, SLOG_FLF) << "Find member(s) by path: " << "path: " << path.serialize(); - - if (0 == path.size()) - { - // empty path - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty path to do FindMembersByPath")); - } - - auto nc_block_member_descriptors = value::array(); - value nc_block_member_descriptor; - - for (const auto& role : path.as_array()) - { - // look for the role in members - if (resource->data.has_field(nmos::fields::nc::members)) - { - auto& members = nmos::fields::nc::members(resource->data); - auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& nc_block_member_descriptor) - { - return role.as_string() == nmos::fields::nc::role(nc_block_member_descriptor); - }); - - if (members.end() != member_found) - { - nc_block_member_descriptor = *member_found; - - // use oid to look for the next resource - resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(*member_found)))); - } - else - { - // no role - utility::stringstream_t ss; - ss << U("role: ") << role.as_string() << U(" not found to do FindMembersByPath"); - return make_control_protocol_error_response(handle, { nc_method_status::bad_oid }, ss.str()); - } - } - else - { - // no members - return make_control_protocol_error_response(handle, { nc_method_status::bad_oid }, U("no members to do FindMembersByPath")); - } - } - - web::json::push_back(nc_block_member_descriptors, nc_block_member_descriptor); - return make_control_protocol_response(handle, { nc_method_status::ok }, nc_block_member_descriptors); - }; - // Finds members with given role name or fragment - const auto find_members_by_role = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes&, const nmos::experimental::datatypes&, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - const auto& role = nmos::fields::nc::role(arguments); // Role text to search for - const auto& case_sensitive = nmos::fields::nc::case_sensitive(arguments); // Signals if the comparison should be case sensitive - const auto& match_whole_string = nmos::fields::nc::match_whole_string(arguments); // TRUE to only return exact matches - const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks - - slog::log(gate, SLOG_FLF) << "Find members with given role name or fragment: " << "role: " << role; - - if (role.empty()) - { - // empty role - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty role to do FindMembersByRole")); - } - - auto descriptors = value::array(); - nmos::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); - - return make_control_protocol_response(handle, { nc_method_status::ok }, descriptors); - }; - // Finds members with given class id - const auto find_members_by_class_id = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const value& arguments, const nmos::experimental::control_classes&, const nmos::experimental::datatypes&, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - const auto& class_id = parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for - const auto& include_derived = nmos::fields::nc::include_derived(arguments); // If TRUE it will also include derived class descriptors - const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks - - slog::log(gate, SLOG_FLF) << "Find members with given class id: " << "class_id: " << nmos::details::make_nc_class_id(class_id).serialize(); - - if (class_id.empty()) - { - // empty class_id - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty classId to do FindMembersByClassId")); - } - - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto descriptors = value::array(); - nmos::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); - - return make_control_protocol_response(handle, { nc_method_status::ok }, descriptors); - }; - - // NcClassManager methods implementation - // Get a single class descriptor - const auto get_control_class = [](nmos::resources&, nmos::resources::iterator, int32_t handle, const value& arguments, const nmos::experimental::control_classes& control_classes, const nmos::experimental::datatypes&, slog::base_gate& gate) - { - const auto& class_id = parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for - const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements - - slog::log(gate, SLOG_FLF) << "Get a single class descriptor: " << "class_id: " << nmos::details::make_nc_class_id(class_id).serialize(); - - if (class_id.empty()) - { - // empty class_id - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty classId to do GetControlClass")); - } - - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - auto class_found = control_classes.find(class_id); - - if (control_classes.end() != class_found) - { - auto id = class_id; - - auto& description = class_found->second.description; - auto& name = class_found->second.name; - auto& fixed_role = class_found->second.fixed_role; - auto properties = class_found->second.properties; - auto methods = class_found->second.methods; - auto events = class_found->second.events; - - id.pop_back(); - - if (include_inherited) - { - while (!id.empty()) - { - auto found = control_classes.find(id); - if (control_classes.end() != found) - { - for (const auto& property : found->second.properties.as_array()) { web::json::push_back(properties, property); } - for (const auto& method : found->second.methods.as_array()) { web::json::push_back(methods, method); } - for (const auto& event : found->second.events.as_array()) { web::json::push_back(events, event); } - } - id.pop_back(); - } - } - auto descriptor = details::make_nc_class_descriptor(description, class_id, name, fixed_role, properties, methods, events); - - return make_control_protocol_response(handle, { nc_method_status::ok }, descriptor); - } - - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("classId not found")); - }; - // Get a single datatype descriptor - const auto get_datatype = [](nmos::resources&, nmos::resources::iterator, int32_t handle, const value& arguments, const nmos::experimental::control_classes&, const nmos::experimental::datatypes& datatypes, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - const auto& name = nmos::fields::nc::name(arguments); // name of datatype - const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements - - slog::log(gate, SLOG_FLF) << "Get a single datatype descriptor: " << "name: " << name; - - if (name.empty()) - { - // empty name - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty name to do GetDatatype")); - } - - auto datatype_found = datatypes.find(name); - - if (datatypes.end() != datatype_found) - { - auto descriptor = datatype_found->second.descriptor; - - if (include_inherited) - { - const auto& type = nmos::fields::nc::type(descriptor); - if (nc_datatype_type::Struct == type) - { - auto descriptor_ = descriptor; - - for (;;) - { - const auto& parent_type = descriptor_.at(nmos::fields::nc::parent_type); - if (!parent_type.is_null()) - { - auto datatype_found_ = datatypes.find(parent_type.as_string()); - if (datatypes.end() != datatype_found_) - { - descriptor_ = datatype_found_->second.descriptor; - const auto& fields = nmos::fields::nc::fields(descriptor_); - for (const auto& field : fields) - { - web::json::push_back(descriptor.at(nmos::fields::nc::fields), field); - } - } - } - else - { - break; - } - } - } - } - - return make_control_protocol_response(handle, { nc_method_status::ok }, descriptor); - } - - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("name not found")); - }; - - // method handlers for the different classes - nmos::experimental::methods nc_object_method_handlers; // method_id vs NcObject method_handler - nmos::experimental::methods nc_block_method_handlers; // method_id vs NcBlock method_handler - nmos::experimental::methods nc_worker_method_handlers; // method_id vs NcWorker method_handler - nmos::experimental::methods nc_manager_method_handlers; // method_id vs NcManager method_handler - nmos::experimental::methods nc_device_manager_method_handlers; // method_id vs NcDeviceManager method_handler - nmos::experimental::methods nc_class_manager_method_handlers; // method_id vs NcClassManager method_handler - - // NcObject methods - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject - nc_object_method_handlers[{1, 1}] = get; - nc_object_method_handlers[{1, 2}] = set; - nc_object_method_handlers[{1, 3}] = get_sequence_item; - nc_object_method_handlers[{1, 4}] = set_sequence_item; - nc_object_method_handlers[{1, 5}] = add_sequence_item; - nc_object_method_handlers[{1, 6}] = remove_sequence_item; - nc_object_method_handlers[{1, 7}] = get_sequence_length; - - // NcBlock methods - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock - nc_block_method_handlers[{2, 1}] = get_member_descriptors; - nc_block_method_handlers[{2, 2}] = find_members_by_path; - nc_block_method_handlers[{2, 3}] = find_members_by_role; - nc_block_method_handlers[{2, 4}] = find_members_by_class_id; - - // NcWorker has no extended method - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker - - // NcManager has no extended method - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager - - // NcDeviceManger has no extended method - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager - - // NcClassManager methods - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - nc_class_manager_method_handlers[{3, 1}] = get_control_class; - nc_class_manager_method_handlers[{3, 2}] = get_datatype; - - // class id vs method handlers - // hmm, todo, custom class and assoicated methods will need to be inserted within follwoing table! - const std::map methods = - { - { nc_object_class_id, nc_object_method_handlers }, - { nc_block_class_id, nc_block_method_handlers }, - { nc_class_manager_class_id, nc_class_manager_method_handlers } - }; - - auto class_id = class_id_; while (!class_id.empty()) @@ -660,15 +61,11 @@ namespace nmos { return method_found->second; } - else - { - //control_classes. - } } class_id.pop_back(); } - return NULL; + return nullptr; } } @@ -808,11 +205,13 @@ namespace nmos }; } - web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_classes_handler get_control_protocol_classes, nmos::get_control_protocol_datatypes_handler get_control_protocol_datatypes, slog::base_gate& gate_) + web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_methods_handler get_control_protocol_methods, slog::base_gate& gate_) { using web::json::value; - return [&model, &websockets, get_control_protocol_classes, get_control_protocol_datatypes, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id, const web::websockets::websocket_incoming_message& msg_) + auto methods = get_control_protocol_methods(); + + return [&model, &websockets, get_control_protocol_class, get_control_protocol_datatype, methods, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id, const web::websockets::websocket_incoming_message& msg_) { nmos::ws_api_gate gate(gate_, connection_uri); @@ -873,11 +272,11 @@ namespace nmos const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); // find the relevent method handler to execute - auto method = details::find_method(method_id, class_id, get_control_protocol_classes()); + auto method = details::find_method(method_id, class_id, methods); if (method) { // execute the relevant method handler, then accumulating up their response to reponses - web::json::push_back(responses, method(resources, resource, handle, arguments, get_control_protocol_classes(), get_control_protocol_datatypes(), gate)); + web::json::push_back(responses, method(resources, resource, handle, arguments, get_control_protocol_class, get_control_protocol_datatype, gate)); } else { diff --git a/Development/nmos/control_protocol_ws_api.h b/Development/nmos/control_protocol_ws_api.h index 6f0494ae1..7fb79a520 100644 --- a/Development/nmos/control_protocol_ws_api.h +++ b/Development/nmos/control_protocol_ws_api.h @@ -16,15 +16,15 @@ namespace nmos web::websockets::experimental::listener::validate_handler make_control_protocol_ws_validate_handler(nmos::node_model& model, slog::base_gate& gate); web::websockets::experimental::listener::open_handler make_control_protocol_ws_open_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate); web::websockets::experimental::listener::close_handler make_control_protocol_ws_close_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate); - web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_classes_handler get_control_protocol_classes, nmos::get_control_protocol_datatypes_handler get_control_protocol_datatypes, slog::base_gate& gate); + web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_methods_handler get_control_protocol_methods, slog::base_gate& gate); - inline web::websockets::experimental::listener::websocket_listener_handlers make_control_protocol_ws_api(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_classes_handler get_control_protocol_classes, nmos::get_control_protocol_datatypes_handler get_control_protocol_datatypes, slog::base_gate& gate) + inline web::websockets::experimental::listener::websocket_listener_handlers make_control_protocol_ws_api(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_methods_handler get_control_protocol_methods, slog::base_gate& gate) { return{ nmos::make_control_protocol_ws_validate_handler(model, gate), nmos::make_control_protocol_ws_open_handler(model, websockets, gate), nmos::make_control_protocol_ws_close_handler(model, websockets, gate), - nmos::make_control_protocol_ws_message_handler(model, websockets, get_control_protocol_classes, get_control_protocol_datatypes, gate) + nmos::make_control_protocol_ws_message_handler(model, websockets, get_control_protocol_class, get_control_protocol_datatype, get_control_protocol_methods, gate) }; } diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index cf4c15054..ccc9d1e3f 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -75,7 +75,7 @@ namespace nmos { if (control_protocol_ws_port == events_ws_port) throw std::runtime_error("Same port used for events and control protocol websockets are not supported"); auto& control_protocol_ws_api = node_server.ws_handlers[{ {}, control_protocol_ws_port }]; - control_protocol_ws_api.first = nmos::make_control_protocol_ws_api(node_model, control_protocol_ws_api.second, node_implementation.get_control_protocol_classes, node_implementation.get_control_protocol_datatypes, gate); + control_protocol_ws_api.first = nmos::make_control_protocol_ws_api(node_model, control_protocol_ws_api.second, node_implementation.get_control_protocol_class, node_implementation.get_control_protocol_datatype, node_implementation.get_control_protocol_methods, gate); } // Set up the listeners for each HTTP API port diff --git a/Development/nmos/node_server.h b/Development/nmos/node_server.h index 4f897a8e0..95fdca058 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -25,7 +25,7 @@ namespace nmos // underlying implementation into the server instance for the NMOS Node struct node_implementation { - node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, nmos::get_control_protocol_classes_handler get_control_protocol_classes, nmos::get_control_protocol_datatypes_handler get_control_protocol_datatypes) + node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_methods_handler get_control_protocol_methods) : load_server_certificates(std::move(load_server_certificates)) , load_dh_param(std::move(load_dh_param)) , load_ca_certificates(std::move(load_ca_certificates)) @@ -37,8 +37,9 @@ namespace nmos , set_transportfile(std::move(set_transportfile)) , connection_activated(std::move(connection_activated)) , get_ocsp_response(std::move(get_ocsp_response)) - , get_control_protocol_classes(std::move(get_control_protocol_classes)) - , get_control_protocol_datatypes(std::move(get_control_protocol_datatypes)) + , get_control_protocol_class(std::move(get_control_protocol_class)) + , get_control_protocol_datatype(std::move(get_control_protocol_datatype)) + , get_control_protocol_methods(std::move(get_control_protocol_methods)) {} // use the default constructor and chaining member functions for fluent initialization @@ -60,8 +61,9 @@ namespace nmos node_implementation& on_validate_channelmapping_output_map(nmos::details::channelmapping_output_map_validator validate_map) { this->validate_map = std::move(validate_map); return *this; } node_implementation& on_channelmapping_activated(nmos::channelmapping_activation_handler channelmapping_activated) { this->channelmapping_activated = std::move(channelmapping_activated); return *this; } node_implementation& on_get_ocsp_response(nmos::ocsp_response_handler get_ocsp_response) { this->get_ocsp_response = std::move(get_ocsp_response); return *this; } - node_implementation& on_get_control_classes(nmos::get_control_protocol_classes_handler get_control_protocol_classes) { this->get_control_protocol_classes = std::move(get_control_protocol_classes); return* this; } - node_implementation& on_get_control_datatypes(nmos::get_control_protocol_datatypes_handler get_control_protocol_datatypes) { this->get_control_protocol_datatypes = std::move(get_control_protocol_datatypes); return*this; } + node_implementation& on_get_control_class(nmos::get_control_protocol_class_handler get_control_protocol_class) { this->get_control_protocol_class = std::move(get_control_protocol_class); return *this; } + node_implementation& on_get_control_datatype(nmos::get_control_protocol_datatype_handler get_control_protocol_datatype) { this->get_control_protocol_datatype = std::move(get_control_protocol_datatype); return *this; } + node_implementation& on_get_control_protocol_methods(nmos::get_control_protocol_methods_handler get_control_protocol_methods) { this->get_control_protocol_methods = std::move(get_control_protocol_methods); return *this; } // deprecated, use on_validate_connection_resource_patch node_implementation& on_validate_merged(nmos::details::connection_resource_patch_validator validate_merged) { return on_validate_connection_resource_patch(std::move(validate_merged)); } @@ -92,8 +94,9 @@ namespace nmos nmos::ocsp_response_handler get_ocsp_response; - nmos::get_control_protocol_classes_handler get_control_protocol_classes; - nmos::get_control_protocol_datatypes_handler get_control_protocol_datatypes; + nmos::get_control_protocol_class_handler get_control_protocol_class; + nmos::get_control_protocol_datatype_handler get_control_protocol_datatype; + nmos::get_control_protocol_methods_handler get_control_protocol_methods; }; // Construct a server instance for an NMOS Node, implementing the IS-04 Node API, IS-05 Connection API, IS-07 Events API From c581781dc57e11a388d42d711512e4af5a8d1246 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 31 Aug 2023 14:15:48 +0100 Subject: [PATCH 037/250] Fix 'nmos::experimental::control_class' constructor initialization --- Development/nmos/control_protocol_state.cpp | 14 +++++----- Development/nmos/control_protocol_state.h | 29 ++++++++++++++++----- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 7376a7ed0..ed18a4a4b 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -14,7 +14,7 @@ namespace nmos // properties: vector of NcPropertyDescriptor which can be constructed using make_control_class_property // methods: vector of NcMethodDescriptor which can be constructed using make_nc_method_descriptor and the assoicated method handler // events: vector of NcEventDescriptor can be constructed using make_nc_event_descriptor - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const web::json::value& fixed_role, const std::vector& properties_, const std::vector>& methods_, const std::vector& events_) + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const std::vector& properties_, const std::vector>& methods_, const std::vector& events_) { using web::json::value; @@ -38,7 +38,7 @@ namespace nmos // properties: vector of NcPropertyDescriptor which can be constructed using make_control_class_property // methods: vector of NcMethodDescriptor which can be constructed using make_nc_method_descriptor and the assoicated method handler // events: vector of NcEventDescriptor can be constructed using make_nc_event_descriptor - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const utility::string_t& fixed_role, const std::vector& properties, const std::vector>& methods, const std::vector& events) + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const std::vector& properties, const std::vector>& methods, const std::vector& events) { using web::json::value; @@ -49,7 +49,7 @@ namespace nmos // properties: vector of NcPropertyDescriptor which can be constructed using make_control_class_property // methods: vector of NcMethodDescriptor which can be constructed using make_nc_method_descriptor and the assoicated method handler // events: vector of NcEventDescriptor can be constructed using make_nc_event_descriptor - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const std::vector& properties, const std::vector>& methods, const std::vector& events) + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const std::vector& properties, const std::vector>& methods, const std::vector& events) { using web::json::value; @@ -57,19 +57,19 @@ namespace nmos } // create control class property - web::json::value make_control_class_property(const utility::string_t& description, const nc_property_id& id, const utility::string_t& name, const utility::string_t& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) + web::json::value make_control_class_property(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) { return nmos::details::make_nc_property_descriptor(description, id, name, type_name, is_read_only, is_nullable, is_sequence, is_deprecated, constraints); } // create control class method parameter - web::json::value make_control_class_method_parameter(const utility::string_t& description, const utility::string_t& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + web::json::value make_control_class_method_parameter(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { return nmos::details::make_nc_parameter_descriptor(description, name, type_name, is_nullable, is_sequence, constraints); } // create control class method - web::json::value make_control_class_method(const utility::string_t& description, const nc_method_id& id, const utility::string_t& name, const utility::string_t& result_datatype, const std::vector& parameters_, bool is_deprecated) + web::json::value make_control_class_method(const utility::string_t& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const std::vector& parameters_, bool is_deprecated) { using web::json::value; @@ -80,7 +80,7 @@ namespace nmos } // create control class event - web::json::value make_control_class_event(const utility::string_t& description, const nc_event_id& id, const utility::string_t& name, const utility::string_t& event_datatype, bool is_deprecated) + web::json::value make_control_class_event(const utility::string_t& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated) { return nmos::details::make_nc_event_descriptor(description, id, name, event_datatype, is_deprecated); } diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index 77b2571d7..319bf1319 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -17,7 +17,7 @@ namespace nmos { web::json::value description; nmos::nc_class_id class_id; - utility::string_t name; + nmos::nc_name name; web::json::value fixed_role; web::json::value properties = web::json::value::array(); // array of NcPropertyDescriptor @@ -25,6 +25,21 @@ namespace nmos web::json::value events = web::json::value::array(); // array of NcEventDescriptor nmos::experimental::methods method_handlers; // map of method handlers which are associated to this control_class (class_id), but not including its base class + + control_class() + : class_id({ 0 }) + {} + + control_class(web::json::value description, nmos::nc_class_id class_id, nmos::nc_name name, web::json::value fixed_role, web::json::value properties, web::json::value methods, web::json::value events, nmos::experimental::methods method_handlers) + : description(std::move(description)) + , class_id(std::move(class_id)) + , name(std::move(name)) + , fixed_role(std::move(fixed_role)) + , properties(std::move(properties)) + , methods(std::move(methods)) + , events(std::move(events)) + , method_handlers(std::move(method_handlers)) + {} }; struct datatype // NcDatatypeDescriptorEnum/NcDatatypeDescriptorPrimitive/NcDatatypeDescriptorStruct/NcDatatypeDescriptorTypeDef @@ -62,24 +77,24 @@ namespace nmos // helper functions to create non-standard control class // // create control class method parameter - web::json::value make_control_class_method_parameter(const utility::string_t& description, const utility::string_t& name, const utility::string_t& type_name, + web::json::value make_control_class_method_parameter(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable = false, bool is_sequence = false, const web::json::value& constraints = web::json::value::null()); // create control class method - web::json::value make_control_class_method(const utility::string_t& description, const nc_method_id& id, const utility::string_t& name, const utility::string_t& result_datatype, + web::json::value make_control_class_method(const utility::string_t& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const std::vector& parameters = {}, bool is_deprecated = false); // create control class event - web::json::value make_control_class_event(const utility::string_t& description, const nc_event_id& id, const utility::string_t& name, const utility::string_t& event_datatype, + web::json::value make_control_class_event(const utility::string_t& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated = false); // create control class property - web::json::value make_control_class_property(const utility::string_t& description, const nc_property_id& id, const utility::string_t& name, const utility::string_t& type_name, + web::json::value make_control_class_property(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, bool is_read_only = false, bool is_nullable = false, bool is_sequence = false, bool is_deprecated = false, const web::json::value& constraints = web::json::value::null()); // create control class with fixed role - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const utility::string_t& fixed_role, const std::vector& properties, const std::vector>& methods, const std::vector& events); + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const std::vector& properties, const std::vector>& methods, const std::vector& events); // create control class with no fixed role - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const utility::string_t& name, const std::vector& properties, const std::vector>& methods, const std::vector& events); + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const std::vector& properties, const std::vector>& methods, const std::vector& events); } } From 3b585069076fcd1db294ff98413e5b62d489ecae Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 7 Sep 2023 14:52:43 +0100 Subject: [PATCH 038/250] Fix indentation --- Development/nmos/control_protocol_methods.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index 8aec5aec1..10da83987 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -354,9 +354,9 @@ namespace nmos { auto& members = nmos::fields::nc::members(resource->data); auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& nc_block_member_descriptor) - { - return role.as_string() == nmos::fields::nc::role(nc_block_member_descriptor); - }); + { + return role.as_string() == nmos::fields::nc::role(nc_block_member_descriptor); + }); if (members.end() != member_found) { From 279358cb5cab208e175ba78fc536df44eda560d8 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 7 Sep 2023 16:36:48 +0100 Subject: [PATCH 039/250] Use better error instead of `out of bounds` --- Development/nmos/control_protocol_methods.cpp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index 10da83987..4359f9fbe 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -88,7 +88,9 @@ namespace nmos const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); if (!property.is_null()) { - if (!nmos::fields::nc::is_sequence(property)) + auto& data = resource->data.at(nmos::fields::nc::name(property)); + + if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) { // property is not a sequence utility::stringstream_t ss; @@ -96,9 +98,7 @@ namespace nmos return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); } - auto& data = resource->data.at(nmos::fields::nc::name(property)); - - if (!data.is_null() && data.as_array().size() > (size_t)index) + if (data.as_array().size() > (size_t)index) { return make_control_protocol_response(handle, { nc_method_status::ok }, data.at(index)); } @@ -130,7 +130,9 @@ namespace nmos const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); if (!property.is_null()) { - if (!nmos::fields::nc::is_sequence(property)) + auto& data = resource->data.at(nmos::fields::nc::name(property)); + + if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) { // property is not a sequence utility::stringstream_t ss; @@ -138,9 +140,7 @@ namespace nmos return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); } - auto& data = resource->data.at(nmos::fields::nc::name(property)); - - if (!data.is_null() && data.as_array().size() > (size_t)index) + if (data.as_array().size() > (size_t)index) { resources.modify(resource, [&](nmos::resource& resource) { @@ -220,7 +220,9 @@ namespace nmos const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); if (!property.is_null()) { - if (!nmos::fields::nc::is_sequence(property)) + auto& data = resource->data.at(nmos::fields::nc::name(property)); + + if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) { // property is not a sequence utility::stringstream_t ss; @@ -228,9 +230,7 @@ namespace nmos return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); } - auto& data = resource->data.at(nmos::fields::nc::name(property)); - - if (!data.is_null() && data.as_array().size() > (size_t)index) + if (data.as_array().size() > (size_t)index) { resources.modify(resource, [&](nmos::resource& resource) { From 431b4a8459e8d8959dfa48be3d9bd9e3d88e02f3 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 12 Sep 2023 10:23:10 +0100 Subject: [PATCH 040/250] Add non-standard Example class based on nmos-device-control-mock --- .../nmos-cpp-node/node_implementation.cpp | 239 ++++++++++++++++-- 1 file changed, 222 insertions(+), 17 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index aa4ab3324..279387d07 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -909,31 +909,216 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // example to create a non-standard Gain control class const auto gain_control_class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 1 }); const web::json::field_as_number gain_value{ U("gainValue") }; - // Gain control class properties - std::vector gain_control_properties = { nmos::experimental::make_control_class_property(U("Gain value"), { 3, 1 }, gain_value, U("NcFloat32")) }; - // Gain control class method example - auto example_method = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { - slog::log(gate, SLOG_FLF) << "Executing the example method"; - return nmos::make_control_protocol_response(handle, { nmos::nc_method_status::ok }); - }; - // Gain control class methods - std::vector> gain_control_methods = - { - { nmos::experimental::make_control_class_method(U("This is an example method"), {3, 1}, U("ExampleMethod"), U("NcMethodResult"), {}, false), example_method } - }; - // create Gain control class - auto gain_control_class = nmos::experimental::make_control_class(U("Gain control class descriptor"), gain_control_class_id, U("GainControl"), gain_control_properties, gain_control_methods, {}); - // insert Gain control class to global state, which will be used by the control_protocol_ws_message_handler to process incoming ws message - control_protocol_state.insert(gain_control_class); - // helper function to create Gain control instance + // Gain control class properties + std::vector gain_control_properties = { nmos::experimental::make_control_class_property(U("Gain value"), { 3, 1 }, gain_value, U("NcFloat32")) }; + + // create Gain control class + auto gain_control_class = nmos::experimental::make_control_class(U("Gain control class descriptor"), gain_control_class_id, U("GainControl"), gain_control_properties); + + // insert Gain control class to global state, which will be used by the control_protocol_ws_message_handler to process incoming ws message + control_protocol_state.insert(gain_control_class); + } + // helper function to create Gain control auto make_gain_control = [&gain_value, &gain_control_class_id](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, float gain = 0.0, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null()) { auto data = nmos::details::make_nc_worker(gain_control_class_id, oid, true, owner, role, value::string(user_label), touchpoints, runtime_property_constraints, true); data[gain_value] = value::number(gain); + return nmos::resource{ nmos::is12_versions::v1_0, nmos::types::nc_worker, std::move(data), true }; }; + // example to create a non-standard Example control class + const auto example_control_class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 2 }); + const web::json::field_as_number enum_property{ U("enumProperty") }; + const web::json::field_as_string string_property{ U("stringProperty") }; + const web::json::field_as_number number_property{ U("numberProperty") }; + const web::json::field_as_bool boolean_property{ U("booleanProperty") }; + const web::json::field_as_value object_property{ U("objectProperty") }; + const web::json::field_as_number method_no_args_count{ U("methodNoArgsCount") }; + const web::json::field_as_number method_simple_args_count{ U("methodSimpleArgsCount") }; + const web::json::field_as_number method_object_arg_count{ U("methodObjectArgCount") }; + const web::json::field_as_array string_sequence{ U("stringSequence") }; + const web::json::field_as_array boolean_sequence{ U("booleanSequence") }; + const web::json::field_as_array enum_sequence{ U("enumSequence") }; + const web::json::field_as_array number_sequence{ U("numberSequence") }; + const web::json::field_as_array object_sequence{ U("objectSequence") }; + const web::json::field_as_number enum_arg{ U("enumArg") }; + const web::json::field_as_string string_arg{ U("stringArg") }; + const web::json::field_as_number number_arg{ U("numberArg") }; + const web::json::field_as_bool boolean_arg{ U("booleanArg") }; + const web::json::field_as_bool obj_arg{ U("objArg") }; + enum example_enum + { + Undefined = 0, + Alpha = 1, + Beta = 2, + Gamma = 3 + }; + + { + // Example control class properties + std::vector example_control_properties = { + nmos::experimental::make_control_class_property(U("Example enum property"), { 3, 1 }, enum_property, U("ExampleEnum")), + // todo constraints + nmos::experimental::make_control_class_property(U("Example string property"), { 3, 2 }, string_property, U("NcString"), false, false, false, false, value::null()), + // todo constraints + nmos::experimental::make_control_class_property(U("Example numeric property"), { 3, 3 }, number_property, U("NcUint64"), false, false, false, false, value::null()), + nmos::experimental::make_control_class_property(U("Example boolean property"), { 3, 4 }, boolean_property, U("NcBoolean")), + nmos::experimental::make_control_class_property(U("Example object property"), { 3, 5 }, object_property, U("ExampleDataType")), + nmos::experimental::make_control_class_property(U("Method no args invoke counter"), { 3, 6 }, method_no_args_count, U("NcUint64"), true), + nmos::experimental::make_control_class_property(U("Method simple args invoke counter"), { 3, 7 }, method_simple_args_count, U("NcUint64"), true), + nmos::experimental::make_control_class_property(U("Method obj arg invoke counter"), { 3, 8 }, method_object_arg_count, U("NcUint64"), true), + nmos::experimental::make_control_class_property(U("Example string sequence property"), { 3, 9 }, string_sequence, U("NcString"), false, false, true), + nmos::experimental::make_control_class_property(U("Example boolean sequence property"), { 3, 10 }, boolean_sequence, U("NcBoolean"), false, false, true), + nmos::experimental::make_control_class_property(U("Example enum sequence property"), { 3, 11 }, enum_sequence, U("ExampleEnum"), false, false, true), + nmos::experimental::make_control_class_property(U("Example number sequence property"), { 3, 12 }, number_sequence, U("NcUint64"), false, false, true), + nmos::experimental::make_control_class_property(U("Example object sequence property"), { 3, 13 }, object_sequence, U("ExampleDataType"), false, false, true) + }; + + // Example control class method handlers + auto example_method_with_no_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + { + slog::log(gate, SLOG_FLF) << "Executing the example method with no arguments"; + return nmos::make_control_protocol_response(handle, { nmos::nc_method_status::ok }); + }; + auto example_method_with_simple_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + { + slog::log(gate, SLOG_FLF) << "Executing the example method with simple arguments"; + return nmos::make_control_protocol_response(handle, { nmos::nc_method_status::ok }); + }; + auto example_method_with_object_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + { + slog::log(gate, SLOG_FLF) << "Executing the example method with object arguments"; + return nmos::make_control_protocol_response(handle, { nmos::nc_method_status::ok }); + }; + // Example control class methods + std::vector> example_control_methods = + { + { nmos::experimental::make_control_class_method(U("Example method with no arguments"), { 3, 1 }, U("MethodNoArgs"), U("NcMethodResult"), {}, false), example_method_with_no_args }, + { nmos::experimental::make_control_class_method(U("Example method with simple arguments"), { 3, 2 }, U("MethodSimpleArgs"), U("NcMethodResult"), + { + nmos::details::make_nc_parameter_descriptor(U("Enum example argument"), enum_arg, U("ExampleEnum"), false, false, value::null()), + nmos::details::make_nc_parameter_descriptor(U("String example argument"), string_arg, U("NcString"), false, false, value::null()), // todo constraints + nmos::details::make_nc_parameter_descriptor(U("Number example argument"), number_arg, U("NcUint64"), false, false, value::null()), // todo constraints + nmos::details::make_nc_parameter_descriptor(U("Boolean example argument"), boolean_arg, U("NcBoolean"), false, false, value::null()) + }, + false), example_method_with_simple_args + }, + { nmos::experimental::make_control_class_method(U("Example method with object argument"), { 3, 3 }, U("MethodObjectArg"), U("NcMethodResult"), + { + nmos::details::make_nc_parameter_descriptor(U("Object example argument"), obj_arg, U("ExampleDataType"), false, false, value::null()) + }, + false), example_method_with_object_args + } + }; + + // create Example control class + auto example_control_class = nmos::experimental::make_control_class(U("Example control class descriptor"), example_control_class_id, U("ExampleControl"), example_control_properties, example_control_methods); + + // insert Example control class to global state, which will be used by the control_protocol_ws_message_handler to process incoming ws message + control_protocol_state.insert(example_control_class); + + // create/insert Example datatypes to global state, which will be used by the control_protocol_ws_message_handler to process incoming ws message + auto make_example_enum_datatype = [&]() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(value::string(U("Undefined")), U("Undefined"), example_enum::Undefined)); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(value::string(U("Alphan")), U("Alpha"), example_enum::Alpha)); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(value::string(U("Beta")), U("Beta"), example_enum::Beta)); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(value::string(U("Gamma")), U("Gamma"), example_enum::Gamma)); + return nmos::details::make_nc_datatype_descriptor_enum(value::string(U("Example enum datatype")), U("ExampleEnum"), items); + }; + auto make_example_datatype_datatype = [&]() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(value::string(U("Enum property example")), enum_property, value::string(U("ExampleEnum")), false, false)); + { + value constraints = value::null(); // todo constraints + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(value::string(U("String property example")), string_property, value::string(U("NcString")), false, false, constraints)); + } + { + value constraints = value::null(); // todo constraints + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(value::string(U("Number property example")), number_property, value::string(U("NcUint64")), false, false, constraints)); + } + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(value::string(U("Boolean property example")), boolean_property, value::string(U("NcBoolean")), false, false)); + return nmos::details::make_nc_datatype_descriptor_struct(value::string(U("Example data type")), U("ExampleDataType"), fields, value::null()); + }; + control_protocol_state.insert(nmos::experimental::datatype{ make_example_enum_datatype() }); + control_protocol_state.insert(nmos::experimental::datatype{ make_example_datatype_datatype() }); + } + // helper function to create Example datatype + auto make_example_datatype = [&](example_enum enum_property_, const utility::string_t& string_property_, uint64_t number_property_, bool boolean_property_) + { + using web::json::value_of; + + return web::json::value_of({ + { enum_property, enum_property_ }, + { string_property, string_property_ }, + { number_property, number_property_ }, + { boolean_property, boolean_property_ } + }); + }; + // helper function to create Example control + auto make_example_control = [&](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, + example_enum enum_property_ = example_enum::Undefined, + const utility::string_t& string_property_ = U(""), + uint64_t number_property_ = 0, + bool boolean_property_ = true, + const value& object_property_ = value::null(), + uint64_t method_no_args_count_ = 0, + uint64_t method_simple_args_count_ = 0, + uint64_t method_object_arg_count_ = 0, + std::vector string_sequence_ = {}, + std::vector boolean_sequence_ = {}, + std::vector enum_sequence_ = {}, + std::vector number_sequence_ = {}, + std::vector object_sequence_ = {}, + const value& touchpoints = value::null(), const value& runtime_property_constraints = value::null()) + { + auto data = nmos::details::make_nc_worker(example_control_class_id, oid, true, owner, role, value::string(user_label), touchpoints, runtime_property_constraints, true); + data[enum_property] = value::number(enum_property_); + data[string_property] = value::string(string_property_); + data[number_property] = value::number(number_property_); + data[boolean_property] = value::boolean(boolean_property_); + data[object_property] = object_property_; + data[method_no_args_count] = value::number(method_no_args_count_); + data[method_simple_args_count] = value::number(method_simple_args_count_); + data[method_object_arg_count] = value::number(method_object_arg_count_); + { + value sequence; + for (const auto& value_ : string_sequence_) { web::json::push_back(sequence, value::string(value_)); } + data[string_sequence] = sequence; + } + { + value sequence; + for (const auto& value_ : boolean_sequence_) { web::json::push_back(sequence, value::boolean(value_)); } + data[boolean_sequence] = sequence; + } + { + value sequence; + for (const auto& value_ : enum_sequence_) { web::json::push_back(sequence, value_); } + data[enum_sequence] = sequence; + } + { + value sequence; + for (const auto& value_ : number_sequence_) { web::json::push_back(sequence, value_); } + data[number_sequence] = sequence; + } + { + value sequence; + for (const auto& value_ : object_sequence_) { web::json::push_back(sequence, value_); } + data[object_sequence] = sequence; + } + + return nmos::resource{ nmos::is12_versions::v1_0, nmos::types::nc_worker, std::move(data), true }; + }; + + // example root block auto root_block = nmos::make_root_block(); @@ -965,6 +1150,25 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr nmos::add_member(U("Master gain block"), master_gain, stereo_gain); nmos::add_member(U("Channel gain block"), channel_gain, stereo_gain); + // example example-control + auto example_control = make_example_control(++oid, nmos::root_block_oid, U("ExampleControl"), U("Example control worker"), + example_enum::Undefined, + U("test"), + 3, + false, + make_example_datatype(example_enum::Undefined, U("default"), 5, false), + 0, + 0, + 0, + { U("red"), U("blue"), U("green") }, + { true, false }, + { example_enum::Alpha, example_enum::Gamma }, + { 0, 50, 80 }, + { make_example_datatype(example_enum::Alpha, U("example"), 50, false), make_example_datatype(example_enum::Gamma, U("different"), 75, true) } + ); + + // add example-control to root-block + nmos::add_member(U("Example control worker"), example_control, root_block); // add stereo-gain to root-block nmos::add_member(U("Stereo gain block"), stereo_gain, root_block); // add class-manager to root-block @@ -973,6 +1177,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr nmos::add_member(U("The device manager offers information about the product this device is representing"), device_manager, root_block); // insert resources to model + if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(example_control), gate)) throw node_implementation_init_exception(); if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(left_gain), gate)) throw node_implementation_init_exception(); if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(right_gain), gate)) throw node_implementation_init_exception(); if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(master_gain), gate)) throw node_implementation_init_exception(); From 0b87a783fef9ffc8b1bd1df8f03bea2cd0be02ee Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 12 Sep 2023 10:24:38 +0100 Subject: [PATCH 041/250] Update log messages --- Development/nmos/control_protocol_handlers.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index 73fa4fa8e..2c3fdac56 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -10,7 +10,7 @@ namespace nmos { return [&](const nc_class_id& class_id) { - slog::log(gate, SLOG_FLF) << "Retrieve control class of class_id: " << nmos::details::make_nc_class_id(class_id).serialize() << " from cache"; + slog::log(gate, SLOG_FLF) << "Retrieve control protocol control class of class_id: " << nmos::details::make_nc_class_id(class_id).serialize() << " from cache"; auto lock = control_protocol_state.read_lock(); @@ -28,7 +28,7 @@ namespace nmos { return [&](const nc_class_id& class_id, const experimental::control_class& control_class) { - slog::log(gate, SLOG_FLF) << "Add control class to cache"; + slog::log(gate, SLOG_FLF) << "Add control protocol control class to cache"; auto lock = control_protocol_state.write_lock(); @@ -47,7 +47,7 @@ namespace nmos { return [&](const nmos::nc_name& name) { - slog::log(gate, SLOG_FLF) << "Retrieve datatype of name: " << name << " from cache"; + slog::log(gate, SLOG_FLF) << "Retrieve control protocol datatype of name: " << name << " from cache"; auto lock = control_protocol_state.read_lock(); @@ -64,7 +64,7 @@ namespace nmos { return [&]() { - slog::log(gate, SLOG_FLF) << "Retrieve all method handlers from cache"; + slog::log(gate, SLOG_FLF) << "Retrieve all control protocol method handlers from cache"; std::map methods; From d8a803ff3ab579796d4a23979503b6037ecf09ae Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 12 Sep 2023 10:25:58 +0100 Subject: [PATCH 042/250] Fix is_nc_block() --- Development/nmos/control_protocol_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 0bc6a9f24..de9e00713 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -28,7 +28,7 @@ namespace nmos // is the given class_id a NcBlock bool is_nc_block(const nc_class_id& class_id) { - return details::is_control_class(nc_object_class_id, class_id); + return details::is_control_class(nc_block_class_id, class_id); } // is the given class_id a NcManager From 9e1fdc832347598d7f41686c83af976b5a804e33 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 12 Sep 2023 10:27:33 +0100 Subject: [PATCH 043/250] Tidy-up function signatures --- Development/nmos/control_protocol_state.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index 319bf1319..3be439b23 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -92,9 +92,9 @@ namespace nmos bool is_read_only = false, bool is_nullable = false, bool is_sequence = false, bool is_deprecated = false, const web::json::value& constraints = web::json::value::null()); // create control class with fixed role - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const std::vector& properties, const std::vector>& methods, const std::vector& events); + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const std::vector& properties = {}, const std::vector>& methods = {}, const std::vector& events = {}); // create control class with no fixed role - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const std::vector& properties, const std::vector>& methods, const std::vector& events); + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const std::vector& properties = {}, const std::vector>& methods = {}, const std::vector& events = {}); } } From 8b4d0d1dff71c6de865062d05d0aef90e8c07210 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 15 Sep 2023 15:19:49 +0100 Subject: [PATCH 044/250] Add subscription support --- .../nmos-cpp-node/node_implementation.cpp | 14 +- Development/nmos/api_utils.cpp | 6 +- Development/nmos/control_protocol_methods.cpp | 78 ++++---- .../nmos/control_protocol_resource.cpp | 173 +++++++++++------- Development/nmos/control_protocol_resource.h | 23 ++- .../nmos/control_protocol_resources.cpp | 62 ++++++- Development/nmos/control_protocol_resources.h | 14 +- Development/nmos/control_protocol_state.cpp | 26 +-- Development/nmos/control_protocol_typedefs.h | 128 ++++++++++++- Development/nmos/control_protocol_ws_api.cpp | 56 ++++-- Development/nmos/json_fields.h | 34 ++-- Development/nmos/query_utils.cpp | 44 +++++ Development/nmos/query_utils.h | 3 + Development/nmos/type.h | 12 +- 14 files changed, 493 insertions(+), 180 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 279387d07..8039782f9 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -925,7 +925,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr auto data = nmos::details::make_nc_worker(gain_control_class_id, oid, true, owner, role, value::string(user_label), touchpoints, runtime_property_constraints, true); data[gain_value] = value::number(gain); - return nmos::resource{ nmos::is12_versions::v1_0, nmos::types::nc_worker, std::move(data), true }; + return nmos::resource{ nmos::is12_versions::v1_0, nmos::types::nc_object, std::move(data), true }; }; // example to create a non-standard Example control class @@ -980,17 +980,17 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr auto example_method_with_no_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { slog::log(gate, SLOG_FLF) << "Executing the example method with no arguments"; - return nmos::make_control_protocol_response(handle, { nmos::nc_method_status::ok }); + return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::ok }); }; auto example_method_with_simple_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { slog::log(gate, SLOG_FLF) << "Executing the example method with simple arguments"; - return nmos::make_control_protocol_response(handle, { nmos::nc_method_status::ok }); + return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::ok }); }; auto example_method_with_object_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { slog::log(gate, SLOG_FLF) << "Executing the example method with object arguments"; - return nmos::make_control_protocol_response(handle, { nmos::nc_method_status::ok }); + return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::ok }); }; // Example control class methods std::vector> example_control_methods = @@ -1026,7 +1026,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr auto items = value::array(); web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(value::string(U("Undefined")), U("Undefined"), example_enum::Undefined)); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(value::string(U("Alphan")), U("Alpha"), example_enum::Alpha)); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(value::string(U("Alpha")), U("Alpha"), example_enum::Alpha)); web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(value::string(U("Beta")), U("Beta"), example_enum::Beta)); web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(value::string(U("Gamma")), U("Gamma"), example_enum::Gamma)); return nmos::details::make_nc_datatype_descriptor_enum(value::string(U("Example enum datatype")), U("ExampleEnum"), items); @@ -1115,7 +1115,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr data[object_sequence] = sequence; } - return nmos::resource{ nmos::is12_versions::v1_0, nmos::types::nc_worker, std::move(data), true }; + return nmos::resource{ nmos::is12_versions::v1_0, nmos::types::nc_object, std::move(data), true }; }; @@ -1204,6 +1204,7 @@ void node_implementation_run(nmos::node_model& model, slog::base_gate& gate) std::shared_ptr events_engine(new std::default_random_engine(events_seeder)); auto cancellation_source = pplx::cancellation_token_source(); + auto token = cancellation_source.get_token(); auto events = pplx::do_while([&model, seed_id, how_many, ws_sender_ports, events_engine, &gate, token] { @@ -1263,6 +1264,7 @@ void node_implementation_run(nmos::node_model& model, slog::base_gate& gate) cancellation_source.cancel(); // wait without the lock since it is also used by the background tasks nmos::details::reverse_lock_guard unlock{ lock }; + events.wait(); } diff --git a/Development/nmos/api_utils.cpp b/Development/nmos/api_utils.cpp index bf352c77c..69d38c5ae 100644 --- a/Development/nmos/api_utils.cpp +++ b/Development/nmos/api_utils.cpp @@ -156,7 +156,8 @@ namespace nmos { U("receivers"), nmos::types::receiver }, { U("subscriptions"), nmos::types::subscription }, { U("inputs"), nmos::types::input }, - { U("outputs"), nmos::types::output } + { U("outputs"), nmos::types::output }, + { U("nc_object"), nmos::types::nc_object } }; return types_from_resourceType.at(resourceType); } @@ -175,7 +176,8 @@ namespace nmos { nmos::types::subscription, U("subscriptions") }, { nmos::types::grain, {} }, // subscription websocket grains aren't exposed via the Query API { nmos::types::input, U("inputs") }, - { nmos::types::output, U("outputs") } + { nmos::types::output, U("outputs") }, + { nmos::types::nc_object, U("nc_object") } }; return resourceTypes_from_type.at(type); } diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index 4359f9fbe..200f8768a 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -2,6 +2,7 @@ #include "cpprest/json_utils.h" #include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_resources.h" #include "nmos/control_protocol_state.h" #include "nmos/control_protocol_utils.h" #include "nmos/json_fields.h" @@ -25,7 +26,7 @@ namespace nmos const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); if (!property.is_null()) { - return make_control_protocol_response(handle, { nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(property))); + return make_control_protocol_message_response(handle, { nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(property))); } // unknown property @@ -50,22 +51,25 @@ namespace nmos { if (nmos::fields::nc::is_read_only(property)) { - return make_control_protocol_response(handle, { nc_method_status::read_only }); + return make_control_protocol_message_response(handle, { nc_method_status::read_only }); } if ((val.is_null() && !nmos::fields::nc::is_nullable(property)) || (val.is_array() && !nmos::fields::nc::is_sequence(property))) { - return make_control_protocol_response(handle, { nc_method_status::parameter_error }); + return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); } - resources.modify(resource, [&](nmos::resource& resource) + const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, { parse_nc_property_id(property_id), nc_property_change_type::type::value_changed, val }); + const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); + + modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) { resource.data[nmos::fields::nc::name(property)] = val; - resource.updated = strictly_increasing_update(resources); - }); - return make_control_protocol_response(handle, { nc_method_status::ok }); + }, notification_event); + + return make_control_protocol_message_response(handle, { nc_method_status::ok }); } // unknown property @@ -100,7 +104,7 @@ namespace nmos if (data.as_array().size() > (size_t)index) { - return make_control_protocol_response(handle, { nc_method_status::ok }, data.at(index)); + return make_control_protocol_message_response(handle, { nc_method_status::ok }, data.at(index)); } // out of bound @@ -142,13 +146,16 @@ namespace nmos if (data.as_array().size() > (size_t)index) { - resources.modify(resource, [&](nmos::resource& resource) + const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, { parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_changed, val, nc_id(index) }); + const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); + + modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) { resource.data[nmos::fields::nc::name(property)][index] = val; - resource.updated = strictly_increasing_update(resources); - }); - return make_control_protocol_response(handle, { nc_method_status::ok }); + }, notification_event); + + return make_control_protocol_message_response(handle, { nc_method_status::ok }); } // out of bound @@ -189,15 +196,19 @@ namespace nmos auto& data = resource->data.at(nmos::fields::nc::name(property)); - resources.modify(resource, [&](nmos::resource& resource) + const nc_id sequence_item_index = data.is_null() ? 0 : nc_id(data.as_array().size()); + const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, { parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_added, val, sequence_item_index }); + const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); + + modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) { auto& sequence = resource.data[nmos::fields::nc::name(property)]; if (data.is_null()) { sequence = value::array(); } web::json::push_back(sequence, val); - resource.updated = strictly_increasing_update(resources); - }); - return make_control_protocol_response(handle, { nc_method_status::ok }, uint32_t(data.as_array().size() - 1)); + }, notification_event); + + return make_control_protocol_message_response(handle, { nc_method_status::ok }, sequence_item_index); } // unknown property @@ -232,14 +243,17 @@ namespace nmos if (data.as_array().size() > (size_t)index) { - resources.modify(resource, [&](nmos::resource& resource) + const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, { parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_removed, data.as_array().at(index), nc_id(index)}); + const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); + + modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) { auto& sequence = resource.data[nmos::fields::nc::name(property)].as_array(); sequence.erase(index); - resource.updated = strictly_increasing_update(resources); - }); - return make_control_protocol_response(handle, { nc_method_status::ok }); + }, notification_event); + + return make_control_protocol_message_response(handle, { nc_method_status::ok }); } // out of bound @@ -285,7 +299,7 @@ namespace nmos if (data.is_null()) { // null - return make_control_protocol_response(handle, { nc_method_status::ok }, value::null()); + return make_control_protocol_message_response(handle, { nc_method_status::ok }, value::null()); } } else @@ -299,7 +313,7 @@ namespace nmos return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); } } - return make_control_protocol_response(handle, { nc_method_status::ok }, uint32_t(data.as_array().size())); + return make_control_protocol_message_response(handle, { nc_method_status::ok }, uint32_t(data.as_array().size())); } // unknown property @@ -323,7 +337,7 @@ namespace nmos auto descriptors = value::array(); nmos::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); - return make_control_protocol_response(handle, { nc_method_status::ok }, descriptors); + return make_control_protocol_message_response(handle, { nc_method_status::ok }, descriptors); } // Finds member(s) by path @@ -344,8 +358,8 @@ namespace nmos return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty path to do FindMembersByPath")); } - auto nc_block_member_descriptors = value::array(); - value nc_block_member_descriptor; + auto descriptors = value::array(); + value descriptor; for (const auto& role : path.as_array()) { @@ -360,7 +374,7 @@ namespace nmos if (members.end() != member_found) { - nc_block_member_descriptor = *member_found; + descriptor = *member_found; // use oid to look for the next resource resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(*member_found)))); @@ -380,8 +394,8 @@ namespace nmos } } - web::json::push_back(nc_block_member_descriptors, nc_block_member_descriptor); - return make_control_protocol_response(handle, { nc_method_status::ok }, nc_block_member_descriptors); + web::json::push_back(descriptors, descriptor); + return make_control_protocol_message_response(handle, { nc_method_status::ok }, descriptors); } // Finds members with given role name or fragment @@ -407,7 +421,7 @@ namespace nmos auto descriptors = value::array(); nmos::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); - return make_control_protocol_response(handle, { nc_method_status::ok }, descriptors); + return make_control_protocol_message_response(handle, { nc_method_status::ok }, descriptors); } // Finds members with given class id @@ -434,7 +448,7 @@ namespace nmos auto descriptors = value::array(); nmos::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); - return make_control_protocol_response(handle, { nc_method_status::ok }, descriptors); + return make_control_protocol_message_response(handle, { nc_method_status::ok }, descriptors); } // NcClassManager methods implementation @@ -482,7 +496,7 @@ namespace nmos } auto descriptor = details::make_nc_class_descriptor(description, class_id, name, fixed_role, properties, methods, events); - return make_control_protocol_response(handle, { nc_method_status::ok }, descriptor); + return make_control_protocol_message_response(handle, { nc_method_status::ok }, descriptor); } return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("classId not found")); @@ -541,7 +555,7 @@ namespace nmos } } - return make_control_protocol_response(handle, { nc_method_status::ok }, descriptor); + return make_control_protocol_message_response(handle, { nc_method_status::ok }, descriptor); } return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("name not found")); diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 5cc1ec3eb..1a9f89507 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -507,68 +507,111 @@ namespace nmos return data; } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertychangedeventdata + web::json::value make_nc_property_changed_event_data(const nc_property_changed_event_data& property_changed_event_data) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::property_id, details::make_nc_property_id(property_changed_event_data.property_id) }, + { nmos::fields::nc::change_type, property_changed_event_data.change_type }, + { nmos::fields::nc::value, property_changed_event_data.value }, + { nmos::fields::nc::sequence_item_index, property_changed_event_data.sequence_item_index } + }); + } } // message response - // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-message-type - web::json::value make_control_protocol_message_response(nc_message_type::type type, const web::json::value& responses) + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#command-response-message-type + web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message) { using web::json::value_of; return value_of({ - { nmos::fields::nc::message_type, type }, - { nmos::fields::nc::responses, responses } + { nmos::fields::nc::handle, handle }, + { nmos::fields::nc::result, details::make_nc_method_result_error(method_result, error_message) } }); } + web::json::value make_control_protocol_message_response(int32_t handle, const nc_method_result& method_result) + { + using web::json::value_of; - // error message - // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#error-messages - web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message) + return value_of({ + { nmos::fields::nc::handle, handle }, + { nmos::fields::nc::result, details::make_nc_method_result(method_result) } + }); + } + web::json::value make_control_protocol_message_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value) { using web::json::value_of; return value_of({ - { nmos::fields::nc::message_type, nc_message_type::error }, - { nmos::fields::nc::status, method_result.status}, - { nmos::fields::nc::error_message, error_message } + { nmos::fields::nc::handle, handle }, + { nmos::fields::nc::result, details::make_nc_method_result(method_result, value) } }); } + web::json::value make_control_protocol_message_response(int32_t handle, const nc_method_result& method_result, uint32_t value_) + { + using web::json::value; - web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message) + return make_control_protocol_message_response(handle, method_result, value(value_)); + } + web::json::value make_control_protocol_message_response(const web::json::value& responses) { using web::json::value_of; return value_of({ - { nmos::fields::nc::handle, handle }, - { nmos::fields::nc::result, details::make_nc_method_result_error(method_result, error_message) } + { nmos::fields::nc::message_type, nc_message_type::command_response }, + { nmos::fields::nc::responses, responses } }); } - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result) + // subscription response + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#subscription-response-message-type + web::json::value make_control_protocol_subscription_response(const web::json::value& subscriptions) { using web::json::value_of; return value_of({ - { nmos::fields::nc::handle, handle }, - { nmos::fields::nc::result, details::make_nc_method_result(method_result) } + { nmos::fields::nc::message_type, nc_message_type::subscription_response }, + { nmos::fields::nc::subscriptions, subscriptions } }); } - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value) + // notification + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#notification-message-type + web::json::value make_control_protocol_notification(nc_oid oid, const nc_event_id& event_id, const nc_property_changed_event_data& property_changed_event_data) { using web::json::value_of; return value_of({ - { nmos::fields::nc::handle, handle }, - { nmos::fields::nc::result, details::make_nc_method_result(method_result, value) } + { nmos::fields::nc::oid, oid }, + { nmos::fields::nc::event_id, details::make_nc_event_id(event_id)}, + { nmos::fields::nc::event_data, details::make_nc_property_changed_event_data(property_changed_event_data) } }); } + web::json::value make_control_protocol_notification(const web::json::value& notifications) + { + using web::json::value_of; - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, uint32_t value_) + return value_of({ + { nmos::fields::nc::message_type, nc_message_type::notification }, + { nmos::fields::nc::notifications, notifications } + }); + } + + // error message + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#error-messages + web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message) { - using web::json::value; + using web::json::value_of; - return make_control_protocol_response(handle, method_result, value(value_)); + return value_of({ + { nmos::fields::nc::message_type, nc_message_type::error }, + { nmos::fields::nc::status, method_result.status}, + { nmos::fields::nc::error_message, error_message } + }); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject @@ -577,14 +620,14 @@ namespace nmos using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Static value. All instances of the same class will have the same identity value"), { 1, 1 }, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Object identifier"), { 1, 2 }, nmos::fields::nc::oid, U("NcOid"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE iff OID is hardwired into device"), { 1, 3 }, nmos::fields::nc::constant_oid, U("NcBoolean"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("OID of containing block. Can only ever be null for the root block"), { 1, 4 }, nmos::fields::nc::owner, U("NcOid"), true, true, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Role of object in the containing block"), { 1, 5 }, nmos::fields::nc::role, U("NcString"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Scribble strip"), { 1, 6 }, nmos::fields::nc::user_label, U("NcString"), false, true, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Touchpoints to other contexts"), { 1, 7 }, nmos::fields::nc::touchpoints, U("NcTouchpoint"), true, true, true, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Runtime property constraints"), { 1, 8 }, nmos::fields::nc::runtime_property_constraints, U("NcPropertyConstraints"), true, true, true, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Static value. All instances of the same class will have the same identity value"), nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Object identifier"), nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE iff OID is hardwired into device"), nc_object_constant_oid_property_id, nmos::fields::nc::constant_oid, U("NcBoolean"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("OID of containing block. Can only ever be null for the root block"), nc_object_owner_property_id, nmos::fields::nc::owner, U("NcOid"), true, true, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Role of object in the containing block"), nc_object_role_property_id, nmos::fields::nc::role, U("NcString"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Scribble strip"), nc_object_user_label_property_id, nmos::fields::nc::user_label, U("NcString"), false, true, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Touchpoints to other contexts"), nc_object_touchpoints_property_id, nmos::fields::nc::touchpoints, U("NcTouchpoint"), true, true, true, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Runtime property constraints"), nc_object_runtime_property_constraints_property_id, nmos::fields::nc::runtime_property_constraints, U("NcPropertyConstraints"), true, true, true, false)); return properties; } @@ -596,43 +639,43 @@ namespace nmos { auto parameters = value::array(); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get property value"), { 1, 1 }, U("Get"), U("NcMethodResultPropertyValue"), parameters, false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get property value"), nc_object_get_method_id, U("Get"), U("NcMethodResultPropertyValue"), parameters, false)); } { auto parameters = value::array(); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property value"), nmos::fields::nc::value, true, false)); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Set property value"), { 1, 2 }, U("Set"), U("NcMethodResult"), parameters, false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Set property value"), nc_object_set_method_id, U("Set"), U("NcMethodResult"), parameters, false)); } { auto parameters = value::array(); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get sequence item"), { 1, 3 }, U("GetSequenceItem"), U("NcMethodResultPropertyValue"), parameters, false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get sequence item"), nc_object_get_sequence_item_method_id, U("GetSequenceItem"), U("NcMethodResultPropertyValue"), parameters, false)); } { auto parameters = value::array(); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false)); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Set sequence item value"), { 1, 4 }, U("SetSequenceItem"), U("NcMethodResult"), parameters, false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Set sequence item value"), nc_object_set_sequence_item_method_id, U("SetSequenceItem"), U("NcMethodResult"), parameters, false)); } { auto parameters = value::array(); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id,U("NcPropertyId"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false)); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Add item to sequence"), { 1, 5 }, U("AddSequenceItem"), U("NcMethodResultId"), parameters, false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Add item to sequence"), nc_object_add_sequence_item_method_id, U("AddSequenceItem"), U("NcMethodResultId"), parameters, false)); } { auto parameters = value::array(); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Delete sequence item"), { 1, 6 }, U("RemoveSequenceItem"), U("NcMethodResult"), parameters, false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Delete sequence item"), nc_object_remove_sequence_item_method_id, U("RemoveSequenceItem"), U("NcMethodResult"), parameters, false)); } { auto parameters = value::array(); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get sequence length"), { 1, 7 }, U("GetSequenceLength"), U("NcMethodResultLength"), parameters, false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get sequence length"), nc_object_get_sequence_length_method_id, U("GetSequenceLength"), U("NcMethodResultLength"), parameters, false)); } return methods; @@ -642,7 +685,7 @@ namespace nmos using web::json::value; auto events = value::array(); - web::json::push_back(events, details::make_nc_event_descriptor(U("Property changed event"), { 1, 1 }, U("PropertyChanged"), U("NcPropertyChangedEventData"), false)); + web::json::push_back(events, details::make_nc_event_descriptor(U("Property changed event"), nc_object_property_changed_event_id, U("PropertyChanged"), U("NcPropertyChangedEventData"), false)); return events; } @@ -653,8 +696,8 @@ namespace nmos using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE if block is functional"), { 2, 1 }, nmos::fields::nc::enabled, U("NcBoolean"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptors of this block's members"), { 2, 2 }, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE if block is functional"), nc_block_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptors of this block's members"), nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false)); return properties; } @@ -666,12 +709,12 @@ namespace nmos { auto parameters = value::array(); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If recurse is set to true, nested members can be retrieved"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Gets descriptors of members of the block"), { 2, 1 }, U("GetMemberDescriptors"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Gets descriptors of members of the block"), nc_block_get_member_descriptors_method_id, U("GetMemberDescriptors"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); } { auto parameters = value::array(); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Relative path to search for (MUST not include the role of the block targeted by oid)"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds member(s) by path"), { 2, 2 }, U("FindMembersByPath"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds member(s) by path"), nc_block_find_members_by_path_method_id, U("FindMembersByPath"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); } { auto parameters = value::array(); @@ -679,14 +722,14 @@ namespace nmos web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Signals if the comparison should be case sensitive"), nmos::fields::nc::case_sensitive, U("NcBoolean"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("TRUE to only return exact matches"), nmos::fields::nc::match_whole_string, U("NcBoolean"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("TRUE to search nested blocks"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds members with given role name or fragment"), { 2, 3 }, U("FindMembersByRole"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds members with given role name or fragment"), nc_block_find_members_by_role_method_id, U("FindMembersByRole"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); } { auto parameters = value::array(); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Class id to search for"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If TRUE it will also include derived class descriptors"), nmos::fields::nc::include_derived, U("NcBoolean"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("TRUE to search nested blocks"), nmos::fields::nc::recurse,U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds members with given class id"), { 2, 4 }, U("FindMembersByClassId"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds members with given class id"), nc_block_find_members_by_class_id_method_id, U("FindMembersByClassId"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); } return methods; @@ -704,7 +747,7 @@ namespace nmos using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE iff worker is enabled"), { 2, 1 }, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE iff worker is enabled"), nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false)); return properties; } @@ -747,16 +790,16 @@ namespace nmos using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Version of MS-05-02 that this device uses"), { 3, 1 }, nmos::fields::nc::nc_version, U("NcVersionCode"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Manufacturer descriptor"), { 3, 2 }, nmos::fields::nc::manufacturer, U("NcManufacturer"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Product descriptor"), { 3, 3 }, nmos::fields::nc::product, U("NcProduct"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Serial number"), { 3, 4 }, nmos::fields::nc::serial_number, U("NcString"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Asset tracking identifier (user specified)"), { 3, 5 }, nmos::fields::nc::user_inventory_code, U("NcString"), false, true, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Name of this device in the application. Instance name, not product name"), { 3, 6 }, nmos::fields::nc::device_name, U("NcString"), false, true, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Role of this device in the application"), { 3, 7 }, nmos::fields::nc::device_role, U("NcString"), false, true, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Device operational state"), { 3, 8 }, nmos::fields::nc::operational_state, U("NcDeviceOperationalState"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Reason for most recent reset"), { 3, 9 }, nmos::fields::nc::reset_cause, U("NcResetCause"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Arbitrary message from dev to controller"), { 3, 10 }, nmos::fields::nc::message, U("NcString"), true, true, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Version of MS-05-02 that this device uses"), nc_device_manager_nc_version_property_id, nmos::fields::nc::nc_version, U("NcVersionCode"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Manufacturer descriptor"), nc_device_manager_manufacturer_property_id, nmos::fields::nc::manufacturer, U("NcManufacturer"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Product descriptor"), nc_device_manager_product_property_id, nmos::fields::nc::product, U("NcProduct"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Serial number"), nc_device_manager_serial_number_property_id, nmos::fields::nc::serial_number, U("NcString"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Asset tracking identifier (user specified)"), nc_device_manager_user_inventory_code_property_id, nmos::fields::nc::user_inventory_code, U("NcString"), false, true, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Name of this device in the application. Instance name, not product name"), nc_device_manager_device_name_property_id, nmos::fields::nc::device_name, U("NcString"), false, true, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Role of this device in the application"), nc_device_manager_device_role_property_id, nmos::fields::nc::device_role, U("NcString"), false, true, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Device operational state"), nc_device_manager_operational_state_property_id, nmos::fields::nc::operational_state, U("NcDeviceOperationalState"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Reason for most recent reset"), nc_device_manager_reset_cause_property_id, nmos::fields::nc::reset_cause, U("NcResetCause"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Arbitrary message from dev to controller"), nc_device_manager_message_property_id, nmos::fields::nc::message, U("NcString"), true, true, false, false)); return properties; } @@ -779,8 +822,8 @@ namespace nmos using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptions of all control classes in the device (descriptors do not contain inherited elements)"), { 3, 1 }, nmos::fields::nc::control_classes, U("NcClassDescriptor"), true, false, true, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptions of all data types in the device (descriptors do not contain inherited elements)"), { 3, 2 }, nmos::fields::nc::datatypes, U("NcDatatypeDescriptor"), true, false, true, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptions of all control classes in the device (descriptors do not contain inherited elements)"), nc_class_manager_control_classes_property_id, nmos::fields::nc::control_classes, U("NcClassDescriptor"), true, false, true, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptions of all data types in the device (descriptors do not contain inherited elements)"), nc_class_manager_datatypes_property_id, nmos::fields::nc::datatypes, U("NcDatatypeDescriptor"), true, false, true, false)); return properties; } @@ -793,13 +836,13 @@ namespace nmos auto parameters = value::array(); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("class ID"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::include_inherited, U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get a single class descriptor"), { 3, 1 }, U("GetControlClass"), U("NcMethodResultClassDescriptor"), parameters, false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get a single class descriptor"), nc_class_manager_get_control_class_method_id, U("GetControlClass"), U("NcMethodResultClassDescriptor"), parameters, false)); } { auto parameters = value::array(); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("name of datatype"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::include_inherited, U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get a single datatype descriptor"), { 3, 2 }, U("GetDatatype"), U("NcMethodResultDatatypeDescriptor"), parameters, false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get a single datatype descriptor"), nc_class_manager_get_datatype_method_id, U("GetDatatype"), U("NcMethodResultDatatypeDescriptor"), parameters, false)); } return methods; @@ -817,10 +860,10 @@ namespace nmos using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status property"), { 3, 1 }, nmos::fields::nc::connection_status, U("NcConnectionStatus"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status message property"), { 3, 2 }, nmos::fields::nc::connection_status_message, U("NcString"), true, true, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Payload status property"), { 3, 3 }, nmos::fields::nc::payload_status, U("NcPayloadStatus"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Payload status message property"), { 3, 4 }, nmos::fields::nc::payload_status_message, U("NcString"), true, true, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status property"), nc_receiver_monitor_connection_status_property_id, nmos::fields::nc::connection_status, U("NcConnectionStatus"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status message property"), nc_receiver_monitor_connection_status_message_property_id, nmos::fields::nc::connection_status_message, U("NcString"), true, true, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Payload status property"), nc_receiver_monitor_payload_status_property_id, nmos::fields::nc::payload_status, U("NcPayloadStatus"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Payload status message property"), nc_receiver_monitor_payload_status_message_property_id, nmos::fields::nc::payload_status_message, U("NcString"), true, true, false, false)); return properties; } @@ -843,7 +886,7 @@ namespace nmos using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Indicates if signal protection is active"), { 4, 1 }, nmos::fields::nc::signal_protection_status, U("NcBoolean"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Indicates if signal protection is active"), nc_receiver_monitor_protected_signal_protection_status_property_id, nmos::fields::nc::signal_protection_status, U("NcBoolean"), true, false, false, false)); return properties; } @@ -866,7 +909,7 @@ namespace nmos using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Indicator active state"), { 3, 1 }, nmos::fields::nc::active, U("NcBoolean"), false, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Indicator active state"), nc_ident_beacon_active_property_id, nmos::fields::nc::active, U("NcBoolean"), false, false, false, false)); return properties; } diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 648b654be..f40234977 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -165,19 +165,26 @@ namespace nmos } // message response - // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-message-type - web::json::value make_control_protocol_message_response(nc_message_type::type type, const web::json::value& responses); + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#command-response-message-type + web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message); + web::json::value make_control_protocol_message_response(int32_t handle, const nc_method_result& method_result); + web::json::value make_control_protocol_message_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value); // value can be sequence, NcClassDescriptor, NcDatatypeDescriptor + web::json::value make_control_protocol_message_response(int32_t handle, const nc_method_result& method_result, uint32_t value); + web::json::value make_control_protocol_message_response(const web::json::value& responses); + + // subscription response + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#subscription-response-message-type + web::json::value make_control_protocol_subscription_response(const web::json::value& subscriptions); + + // notification + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#notification-message-type + web::json::value make_control_protocol_notification(nc_oid oid, const nc_event_id& event_id, const nc_property_changed_event_data& property_changed_event_data); + web::json::value make_control_protocol_notification(const web::json::value& notifications); // error message // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#error-messages web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message); - // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#command-response-message-type - web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message); - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result); - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value); // value can be sequence, NcClassDescriptor, NcDatatypeDescriptor - web::json::value make_control_protocol_response(int32_t handle, const nc_method_result& method_result, uint32_t value); - // Control class models // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/#control-class-models-for-branch-v10-dev // diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index e3a5c5f5a..48b6e7d1f 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -2,6 +2,7 @@ #include "nmos/control_protocol_resource.h" #include "nmos/control_protocol_utils.h" +#include "nmos/query_utils.h" #include "nmos/resource.h" #include "nmos/is12_versions.h" @@ -10,18 +11,18 @@ namespace nmos namespace details { // create block resource - nmos::resource make_block(nmos::nc_oid oid, const web::json::value& owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) + resource make_block(nmos::nc_oid oid, const web::json::value& owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) { using web::json::value; auto data = details::make_nc_block(nc_block_class_id, oid, true, owner, role, value::string(user_label), touchpoints, runtime_property_constraints, true, members); - return{ is12_versions::v1_0, types::nc_block, std::move(data), true }; + return{ is12_versions::v1_0, types::nc_object, std::move(data), true }; } } // create block resource - nmos::resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) + resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) { using web::json::value; @@ -29,7 +30,7 @@ namespace nmos } // create Root block resource - nmos::resource make_root_block() + resource make_root_block() { using web::json::value; @@ -37,7 +38,7 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager - nmos::resource make_device_manager(nc_oid oid, const nmos::settings& settings) + resource make_device_manager(nc_oid oid, const nmos::settings& settings) { using web::json::value; @@ -52,11 +53,11 @@ namespace nmos auto data = details::make_nc_device_manager(oid, root_block_oid, user_label, value::null(), value::null(), manufacturer, product, serial_number, value::null(), device_name, device_role, operational_state, nc_reset_cause::unknown); - return{ is12_versions::v1_0, types::nc_device_manager, std::move(data), true }; + return{ is12_versions::v1_0, types::nc_object, std::move(data), true }; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager - nmos::resource make_class_manager(nc_oid oid, const nmos::experimental::control_protocol_state& control_protocol_state) + resource make_class_manager(nc_oid oid, const nmos::experimental::control_protocol_state& control_protocol_state) { using web::json::value; @@ -64,7 +65,7 @@ namespace nmos auto data = details::make_nc_class_manager(oid, root_block_oid, user_label, value::null(), value::null(), control_protocol_state); - return{ is12_versions::v1_0, types::nc_class_manager, std::move(data), true }; + return{ is12_versions::v1_0, types::nc_object, std::move(data), true }; } // add to owner block member @@ -80,4 +81,49 @@ namespace nmos return true; } + + // modify a resource, and insert notification event to all subscriptions + bool modify_control_protocol_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event) + { + auto found = resources.find(id); + if (resources.end() == found || !found->has_data()) return false; + + auto pre = found->data; + + // "If an exception is thrown by some user-provided operation, then the element pointed to by position is erased." + // This seems too surprising, despite the fact that it means that a modification may have been partially completed, + // so capture and rethrow. + // See https://www.boost.org/doc/libs/1_68_0/libs/multi_index/doc/reference/ord_indices.html#modify + std::exception_ptr modifier_exception; + + auto resource_updated = nmos::strictly_increasing_update(resources); + auto result = resources.modify(found, [&resource_updated, &modifier, &modifier_exception](resource& resource) + { + try + { + modifier(resource); + } + catch (...) + { + modifier_exception = std::current_exception(); + } + + // set the update timestamp + resource.updated = resource_updated; + }); + + if (result) + { + auto& modified = *found; + + insert_notification_events(resources, modified.version, modified.downgrade_version, modified.type, pre, modified.data, notification_event); + } + + if (modifier_exception) + { + std::rethrow_exception(modifier_exception); + } + + return result; + } } diff --git a/Development/nmos/control_protocol_resources.h b/Development/nmos/control_protocol_resources.h index d3fdb44b1..56f80103f 100644 --- a/Development/nmos/control_protocol_resources.h +++ b/Development/nmos/control_protocol_resources.h @@ -2,6 +2,7 @@ #define NMOS_CONTROL_PROTOCOL_RESOURCES_H #include "nmos/control_protocol_typedefs.h" // for details::nc_oid definition +#include "nmos/resources.h" #include "nmos/settings.h" namespace nmos @@ -14,19 +15,22 @@ namespace nmos struct resource; // create block resource - nmos::resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), const web::json::value& members = web::json::value::array()); + resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), const web::json::value& members = web::json::value::array()); // create Root block resource - nmos::resource make_root_block(); + resource make_root_block(); // create Device manager resource - nmos::resource make_device_manager(nc_oid oid, const nmos::settings& settings); + resource make_device_manager(nc_oid oid, const nmos::settings& settings); // create Class manager resource - nmos::resource make_class_manager(nc_oid oid, const nmos::experimental::control_protocol_state& control_protocol_state); + resource make_class_manager(nc_oid oid, const nmos::experimental::control_protocol_state& control_protocol_state); // add to owner block member - bool add_member(const utility::string_t& child_description, const nmos::resource& child_block, nmos::resource& parent_block); + bool add_member(const utility::string_t& child_description, const resource& child_block, resource& parent_block); + + // modify a resource, and insert notification event to all subscriptions + bool modify_control_protocol_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event); } #endif diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index ed18a4a4b..c308ad26d 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -120,22 +120,22 @@ namespace nmos { nc_object_class_id, make_control_class(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), to_vector(make_nc_object_properties()), to_methods_vector(make_nc_object_methods(), { - { {1, 1}, nmos::details::get }, - { {1, 2}, nmos::details::set }, - { {1, 3}, nmos::details::get_sequence_item }, - { {1, 4}, nmos::details::set_sequence_item }, - { {1, 5}, nmos::details::add_sequence_item }, - { {1, 6}, nmos::details::remove_sequence_item }, - { {1, 7}, nmos::details::get_sequence_length } + { nc_object_get_method_id, nmos::details::get }, + { nc_object_set_method_id, nmos::details::set }, + { nc_object_get_sequence_item_method_id, nmos::details::get_sequence_item }, + { nc_object_set_sequence_item_method_id, nmos::details::set_sequence_item }, + { nc_object_add_sequence_item_method_id, nmos::details::add_sequence_item }, + { nc_object_remove_sequence_item_method_id, nmos::details::remove_sequence_item }, + { nc_object_get_sequence_length_method_id, nmos::details::get_sequence_length } }), to_vector(make_nc_object_events())) }, { nc_block_class_id, make_control_class(U("NcBlock class descriptor"), nc_block_class_id, U("NcBlock"), to_vector(make_nc_block_properties()), to_methods_vector(make_nc_block_methods(), { - { {2, 1}, nmos::details::get_member_descriptors }, - { {2, 2}, nmos::details::find_members_by_path }, - { {2, 3}, nmos::details::find_members_by_role }, - { {2, 4}, nmos::details::find_members_by_class_id } + { nc_block_get_member_descriptors_method_id, nmos::details::get_member_descriptors }, + { nc_block_find_members_by_path_method_id, nmos::details::find_members_by_path }, + { nc_block_find_members_by_role_method_id, nmos::details::find_members_by_role }, + { nc_block_find_members_by_class_id_method_id, nmos::details::find_members_by_class_id } }), to_vector(make_nc_block_events())) }, { nc_worker_class_id, make_control_class(U("NcWorker class descriptor"), nc_worker_class_id, U("NcWorker"), to_vector(make_nc_worker_properties()), to_methods_vector(make_nc_worker_methods(), {}), to_vector(make_nc_worker_events())) }, @@ -144,8 +144,8 @@ namespace nmos { nc_class_manager_class_id, make_control_class(U("NcClassManager class descriptor"), nc_class_manager_class_id, U("NcClassManager"), U("ClassManager"), to_vector(make_nc_class_manager_properties()), to_methods_vector(make_nc_class_manager_methods(), { - { {3, 1}, nmos::details::get_control_class }, - { {3, 2}, nmos::details::get_datatype } + { nc_class_manager_get_control_class_method_id, nmos::details::get_control_class }, + { nc_class_manager_get_datatype_method_id, nmos::details::get_datatype } }), to_vector(make_nc_class_manager_events())) }, // identification beacon model diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index bf33a331a..9c0962742 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -2,14 +2,7 @@ #define NMOS_CONTROL_PROTOCOL_TYPEDEFS_H #include "cpprest/basic_utils.h" - -namespace web -{ - namespace json - { - class value; - } -} +#include "cpprest/json_utils.h" namespace nmos { @@ -146,14 +139,81 @@ namespace nmos // NcEventId // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventid typedef nc_element_id nc_event_id; + // NcEventIds for NcObject + // SEe https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject + const nc_event_id nc_object_property_changed_event_id(1, 1); // NcMethodId // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodid typedef nc_element_id nc_method_id; + // NcMethodIds for NcObject + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject + const nc_method_id nc_object_get_method_id(1, 1); + const nc_method_id nc_object_set_method_id(1, 2); + const nc_method_id nc_object_get_sequence_item_method_id(1, 3); + const nc_method_id nc_object_set_sequence_item_method_id(1, 4); + const nc_method_id nc_object_add_sequence_item_method_id(1, 5); + const nc_method_id nc_object_remove_sequence_item_method_id(1, 6); + const nc_method_id nc_object_get_sequence_length_method_id(1, 7); + // NcMethodIds for NcBlock + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock + const nc_method_id nc_block_get_member_descriptors_method_id(2, 1); + const nc_method_id nc_block_find_members_by_path_method_id(2, 2); + const nc_method_id nc_block_find_members_by_role_method_id(2, 3); + const nc_method_id nc_block_find_members_by_class_id_method_id(2, 4); + // NcMethodIds for NcClassManager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager + const nc_method_id nc_class_manager_get_control_class_method_id(3, 1); + const nc_method_id nc_class_manager_get_datatype_method_id(3, 2); // NcPropertyId // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertyid typedef nc_element_id nc_property_id; + // NcPropertyIds for NcObject + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject + const nc_property_id nc_object_class_id_property_id(1, 1); + const nc_property_id nc_object_oid_property_id(1, 2); + const nc_property_id nc_object_constant_oid_property_id(1, 3); + const nc_property_id nc_object_owner_property_id(1, 4); + const nc_property_id nc_object_role_property_id(1, 5); + const nc_property_id nc_object_user_label_property_id(1, 6); + const nc_property_id nc_object_touchpoints_property_id(1, 7); + const nc_property_id nc_object_runtime_property_constraints_property_id(1, 8); + // NcPropertyIds for NcBlock + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock + const nc_property_id nc_block_enabled_property_id(2, 1); + const nc_property_id nc_block_members_property_id(2, 2); + // NcPropertyIds for NcWorker + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker + const nc_property_id nc_worker_enabled_property_id(2, 1); + // NcPropertyIds for NcDeviceManager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager + const nc_property_id nc_device_manager_nc_version_property_id(3, 1); + const nc_property_id nc_device_manager_manufacturer_property_id(3, 2); + const nc_property_id nc_device_manager_product_property_id(3, 3); + const nc_property_id nc_device_manager_serial_number_property_id(3, 4); + const nc_property_id nc_device_manager_user_inventory_code_property_id(3, 5); + const nc_property_id nc_device_manager_device_name_property_id(3, 6); + const nc_property_id nc_device_manager_device_role_property_id(3, 7); + const nc_property_id nc_device_manager_operational_state_property_id(3, 8); + const nc_property_id nc_device_manager_reset_cause_property_id(3, 9); + const nc_property_id nc_device_manager_message_property_id(3, 10); + // NcPropertyIds for NcClassManager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager + const nc_property_id nc_class_manager_control_classes_property_id(3, 1); + const nc_property_id nc_class_manager_datatypes_property_id(3, 2); + // NcPropertyids for NcReceiverMonitor + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor + const nc_property_id nc_receiver_monitor_connection_status_property_id(3, 1); + const nc_property_id nc_receiver_monitor_connection_status_message_property_id(3, 2); + const nc_property_id nc_receiver_monitor_payload_status_property_id(3, 3); + const nc_property_id nc_receiver_monitor_payload_status_message_property_id(3, 4); + // NcPropertyids for NcReceiverMonitorProtected + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitorprotected + const nc_property_id nc_receiver_monitor_protected_signal_protection_status_property_id(4, 1); + // NcPropertyids for NcIdentBeacon + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon + const nc_property_id nc_ident_beacon_active_property_id(3, 1); // NcId // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncid @@ -166,6 +226,7 @@ namespace nmos // NcOid // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncoid typedef uint32_t nc_oid; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Blocks.html const nc_oid root_block_oid{ 1 }; // NcUri @@ -179,19 +240,70 @@ namespace nmos // NcClassId // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid typedef std::vector nc_class_id; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject const nc_class_id nc_object_class_id({ 1 }); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock const nc_class_id nc_block_class_id({ 1, 1 }); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker const nc_class_id nc_worker_class_id({ 1, 2 }); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager const nc_class_id nc_manager_class_id({ 1, 3 }); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager const nc_class_id nc_device_manager_class_id({ 1, 3, 1 }); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager const nc_class_id nc_class_manager_class_id({ 1, 3, 2 }); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon const nc_class_id nc_ident_beacon_class_id({ 1, 2, 2 }); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor const nc_class_id nc_receiver_monitor_class_id({ 1, 2, 3 }); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitorprotected const nc_class_id nc_receiver_monitor_protected_class_id({ 1, 2, 3, 1 }); // NcTouchpoint // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nctouchpoint typedef utility::string_t nc_touch_point; + + // NcPropertyChangeType + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertychangetype + namespace nc_property_change_type + { + enum type + { + value_changed = 0, // Current value changed + sequence_item_added = 1, // Sequence item added + sequence_item_changed = 2, // Sequence item changed + sequence_item_removed = 3 // Sequence item removed + }; + } + + // NcPropertyChangedEventData + // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertychangedeventdata + struct nc_property_changed_event_data + { + nc_property_id property_id; + nc_property_change_type::type change_type; + web::json::value value; + web::json::value sequence_item_index; // nc_id, can be null + + nc_property_changed_event_data(nc_property_id property_id, nc_property_change_type::type change_type, web::json::value value, nc_id sequence_item_index) + : property_id(std::move(property_id)) + , change_type(change_type) + , value(std::move(value)) + , sequence_item_index(sequence_item_index) + {} + + nc_property_changed_event_data(nc_property_id property_id, nc_property_change_type::type change_type, web::json::value value) + : property_id(std::move(property_id)) + , change_type(change_type) + , value(std::move(value)) + , sequence_item_index(web::json::value::null()) + {} + + auto tied() const -> decltype(std::tie(property_id, change_type, value, sequence_item_index)) { return std::tie(property_id, change_type, value, sequence_item_index); } + friend bool operator==(const nc_property_changed_event_data& lhs, const nc_property_changed_event_data& rhs) { return lhs.tied() == rhs.tied(); } + friend bool operator!=(const nc_property_changed_event_data& lhs, const nc_property_changed_event_data& rhs) { return !(lhs == rhs); } + friend bool operator<(const nc_property_changed_event_data& lhs, const nc_property_changed_event_data& rhs) { return lhs.tied() < rhs.tied(); } + }; } #endif diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index bca663ee2..2fd5e1c67 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -1,5 +1,6 @@ #include "nmos/control_protocol_ws_api.h" +#include #include #include "cpprest/json_validator.h" #include "nmos/api_utils.h" @@ -21,7 +22,7 @@ namespace nmos static const web::json::experimental::json_validator validator { nmos::experimental::load_json_schema, - boost::copy_range>(boost::join(boost::join( + boost::copy_range>(boost::range::join(boost::range::join( is12_versions::all | boost::adaptors::transformed(experimental::make_controlprotocolapi_base_message_schema_uri), is12_versions::all | boost::adaptors::transformed(experimental::make_controlprotocolapi_command_message_schema_uri)), is12_versions::all | boost::adaptors::transformed(experimental::make_controlprotocolapi_subscription_message_schema_uri) @@ -121,7 +122,7 @@ namespace nmos value data = value_of({ { nmos::fields::id, nmos::make_id() }, { nmos::fields::max_update_rate_ms, 0 }, - { nmos::fields::resource_path, U('/') + nmos::resourceType_from_type(nmos::types::source) }, + { nmos::fields::resource_path, U('/') + nmos::resourceType_from_type(nmos::types::nc_object) }, { nmos::fields::params, value_of({ { U("query.rql"), U("in(id,())") } }) }, { nmos::fields::persist, non_persistent }, { nmos::fields::secure, secure }, @@ -154,7 +155,7 @@ namespace nmos websockets.insert({ id, connection_id }); - slog::log(gate, SLOG_FLF) << "Creating websocket connection: " << id; + slog::log(gate, SLOG_FLF) << "Creating websocket connection: " << id << " to subscription: " << subscription->id; slog::log(gate, SLOG_FLF) << "Notifying control protocol websockets thread"; // and anyone else who cares... model.notify(); @@ -180,7 +181,7 @@ namespace nmos if (resources.end() != grain) { - slog::log(gate, SLOG_FLF) << "Deleting websocket connection"; + slog::log(gate, SLOG_FLF) << "Deleting websocket connection: " << grain->id; // subscriptions have a 1-1 relationship with the websocket connection and both should now be erased immediately auto subscription = find_resource(resources, { nmos::fields::subscription_id(grain->data), nmos::types::subscription }); @@ -195,7 +196,6 @@ namespace nmos // a grain without a subscription shouldn't be possible, but let's be tidy erase_resource(resources, grain->id); } - //erase_resource(resources, grain->id); } websockets.right.erase(websocket); @@ -208,6 +208,7 @@ namespace nmos web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_methods_handler get_control_protocol_methods, slog::base_gate& gate_) { using web::json::value; + using web::json::value_of; auto methods = get_control_protocol_methods(); @@ -296,11 +297,10 @@ namespace nmos } } - // add command_response for the control protocol response thread to return to the client + // add command_response to the grain ready to transfer to the client in nmos::send_control_protocol_ws_messages_thread resources.modify(grain, [&](nmos::resource& grain) { - web::json::push_back(nmos::fields::message_grain_data(grain.data), - make_control_protocol_message_response(nc_message_type::command_response, responses)); + web::json::push_back(nmos::fields::message_grain_data(grain.data), make_control_protocol_message_response(responses)); grain.updated = strictly_increasing_update(resources); }); @@ -308,11 +308,45 @@ namespace nmos break; case nc_message_type::subscription: { - // hmm, todo... + // validate subscription-message + details::validate_controlprotocolapi_subscription_message_schema(version, message); + + // subscribing to multiple OIDs, and filtering out invalid OIDs which cannot be subscribed to + auto& subscriptions = nmos::fields::nc::subscriptions(message); + value valid_subscriptions = value::array(); + for (const auto& subscription : subscriptions) + { + const auto oid = subscription.as_integer(); + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != resource) + { + // only add the valid OIDs which can be subscribed to + web::json::push_back(valid_subscriptions, subscription); + } + } + + // update the subscription + modify_resource(resources, subscription->id, [&valid_subscriptions](nmos::resource& resource) + { + auto rql_query = U("in(id,(") + boost::algorithm::join(valid_subscriptions.as_array() | boost::adaptors::transformed([](const value& v) { return U("string:") + utility::s2us(std::to_string(v.as_integer())); }), U(",")) + U("))"); + + resource.data[nmos::fields::params] = value_of({ { U("query.rql"), rql_query } }); + }); + + // add subscription_response to the grain ready to transfer to the client in nmos::send_control_protocol_ws_messages_thread + resources.modify(grain, [&](nmos::resource& grain) + { + web::json::push_back(nmos::fields::message_grain_data(grain.data), make_control_protocol_subscription_response(valid_subscriptions)); + + grain.updated = strictly_increasing_update(resources); + }); + + slog::log(gate, SLOG_FLF) << "Received subscription command for " << valid_subscriptions.serialize(); + model.notify(); } - break; + break; default: - // unexpected message type + // ignore unexpected message type break; } diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index df831eaec..0c293ac97 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -238,6 +238,7 @@ namespace nmos // for control_protocol_ws_api commands const web::json::field_as_array commands{ U("commands") }; + const web::json::field_as_array subscriptions{ U("subscriptions")}; const web::json::field_as_integer oid{ U("oid") }; const web::json::field_as_value method_id{ U("methodId") }; const web::json::field_as_value arguments{ U("arguments") }; @@ -255,6 +256,11 @@ namespace nmos // for control_protocol_ws_api commands & responses const web::json::field_as_integer handle{ U("handle") }; + // for cntrol_protocol_ws_api notifications + const web::json::field_as_array notifications{ U("notifications") }; + const web::json::field_as_value event_data{ U("eventData") }; + const web::json::field_as_value event_id{ U("eventId") }; + const web::json::field_as_array class_id{ U("classId") }; const web::json::field_as_bool constant_oid{ U("constantOid") }; const web::json::field_as_integer owner{ U("owner") }; @@ -265,39 +271,39 @@ namespace nmos const web::json::field_as_bool recurse{ U("recurse") }; const web::json::field_as_bool enabled{ U("enabled") }; const web::json::field_as_array members{ U("members") }; - const web::json::field_as_string description{ U("description") }; // can be null - const web::json::field_as_string nc_version{ U("ncVersion") }; // NcVersionCode, string + const web::json::field_as_string description{ U("description") }; + const web::json::field_as_string nc_version{ U("ncVersion") }; // NcVersionCode const web::json::field_as_value manufacturer{ U("manufacturer") }; // NcManufacturer const web::json::field_as_value product{ U("product") }; // NcProduct const web::json::field_as_string serial_number{ U("serialNumber") }; - const web::json::field_as_string user_inventory_code{ U("userInventoryCode") }; // string, can be null - const web::json::field_as_string device_name{ U("deviceName") }; // string, can be null - const web::json::field_as_string device_role{ U("deviceRole") }; // string, can be null + const web::json::field_as_string user_inventory_code{ U("userInventoryCode") }; + const web::json::field_as_string device_name{ U("deviceName") }; + const web::json::field_as_string device_role{ U("deviceRole") }; const web::json::field_as_value operational_state{ U("operationalState") }; // NcDeviceOperationalState const web::json::field_as_integer reset_cause{ U("resetCause") }; // NcResetCause - const web::json::field_as_string message{ U("message") }; // string, can be null + const web::json::field_as_string message{ U("message") }; const web::json::field_as_array control_classes{ U("controlClasses") }; // sequence const web::json::field_as_array datatypes{ U("datatypes") }; // sequence const web::json::field_as_string name{ U("name")}; - const web::json::field_as_string fixed_role{ U("fixedRole") }; // string, can be null + const web::json::field_as_string fixed_role{ U("fixedRole") }; const web::json::field_as_array properties{ U("properties") }; // sequence const web::json::field_as_array methods{ U("methods") }; // sequence const web::json::field_as_array events{ U("events") }; // sequence const web::json::field_as_integer type{ U("type") }; // NcDatatypeType - const web::json::field_as_value constraints{ U("constraints") }; // NcParameterConstraints, can be null + const web::json::field_as_value constraints{ U("constraints") }; // NcParameterConstraints const web::json::field_as_integer organization_id{ U("organizationId") }; const web::json::field_as_string website{ U("website") }; const web::json::field_as_string key{ U("key") }; const web::json::field_as_string revision_level{ U("revisionLevel") }; - const web::json::field_as_string brand_name{ U("brandName") }; // string, can be null - const web::json::field_as_string uuid{ U("uuid") }; // string, can be null - const web::json::field_as_string type_name{ U("typeName") }; // string, can be null + const web::json::field_as_string brand_name{ U("brandName") }; + const web::json::field_as_string uuid{ U("uuid") }; + const web::json::field_as_string type_name{ U("typeName") }; const web::json::field_as_bool is_read_only{ U("isReadOnly") }; const web::json::field_as_bool is_persistent{ U("isPersistent") }; const web::json::field_as_bool is_nullable{ U("isNullable") }; const web::json::field_as_bool is_sequence{ U("isSequence") }; const web::json::field_as_bool is_deprecated{ U("isDeprecated") }; - const web::json::field_as_bool is_constant{ U("isConstant") }; // bool, can be null + const web::json::field_as_bool is_constant{ U("isConstant") }; const web::json::field_as_string parent_type{ U("parentType") }; const web::json::field_as_string event_datatype{ U("eventDatatype") }; const web::json::field_as_string result_datatype{ U("resultDatatype") }; @@ -305,7 +311,7 @@ namespace nmos const web::json::field_as_array items{ U("items") }; // sequence const web::json::field_as_array fields{ U("fields") }; // sequence const web::json::field_as_integer generic_state{ U("generic") }; // NcDeviceGenericState - const web::json::field_as_string device_specific_details{ U("deviceSpecificDetails") }; // string, can be null + const web::json::field_as_string device_specific_details{ U("deviceSpecificDetails") }; const web::json::field_as_array path{ U("path") }; // NcRolePath const web::json::field_as_bool case_sensitive{ U("caseSensitive") }; const web::json::field_as_bool match_whole_string{ U("matchWholeString") }; @@ -314,7 +320,7 @@ namespace nmos const web::json::field_as_string context_namespace{ U("contextNamespace") }; const web::json::field_as_value default_value{ U("defaultValue") }; const web::json::field_as_integer change_type{ U("changeType") }; // NcPropertyChangeType - const web::json::field_as_integer sequence_item_index{ U("sequenceItemIndex") }; // NcId, can be null + const web::json::field_as_integer sequence_item_index{ U("sequenceItemIndex") }; // NcId const web::json::field_as_value property_id{ U("propertyId") }; const web::json::field_as_integer maximum{ U("maximum") }; const web::json::field_as_integer minimum{ U("minimum") }; diff --git a/Development/nmos/query_utils.cpp b/Development/nmos/query_utils.cpp index 14e381200..672e610e6 100644 --- a/Development/nmos/query_utils.cpp +++ b/Development/nmos/query_utils.cpp @@ -577,4 +577,48 @@ namespace nmos } } } + + // insert 'value changed', 'sequence item added', 'sequence item changed' or 'sequence item removed' notification events into all grains whose subscriptions match the specified version, type and "pre" or "post" values + void insert_notification_events(nmos::resources& resources, const nmos::api_version& version, const nmos::api_version& downgrade_version, const nmos::type& type, const web::json::value& pre, const web::json::value& post, const web::json::value& event) + { + using web::json::value; + + if (pre == post) return; + + if (!details::is_queryable_resource(type)) return; + + auto& by_type = resources.get(); + const auto subscriptions = by_type.equal_range(details::has_data(nmos::types::subscription)); + + for (auto it = subscriptions.first; subscriptions.second != it; ++it) + { + // for each subscription + const auto& subscription = *it; + + // check whether the resource_path matches the resource type and the query parameters match either the "pre" or "post" resource + + const auto resource_path = nmos::fields::resource_path(subscription.data); + const resource_query match(subscription.version, resource_path, nmos::fields::params(subscription.data)); + + const bool pre_match = match(version, downgrade_version, type, pre, resources); + const bool post_match = match(version, downgrade_version, type, post, resources); + + if (!pre_match && !post_match) continue; + + // add the event to the grain for each websocket connection to this subscription + + for (const auto& id : subscription.sub_resources) + { + auto grain = find_resource(resources, { id, nmos::types::grain }); + if (resources.end() == grain) continue; // check websocket connection is still open + + resources.modify(grain, [&resources, &event](nmos::resource& grain) + { + auto& events = nmos::fields::message_grain_data(grain.data); + web::json::push_back(events, event); + grain.updated = strictly_increasing_update(resources); + }); + } + } + } } diff --git a/Development/nmos/query_utils.h b/Development/nmos/query_utils.h index 91addbe46..fcfbe9c0b 100644 --- a/Development/nmos/query_utils.h +++ b/Development/nmos/query_utils.h @@ -114,6 +114,9 @@ namespace nmos // insert 'added', 'removed' or 'modified' resource events into all grains whose subscriptions match the specified version, type and "pre" or "post" values void insert_resource_events(nmos::resources& resources, const nmos::api_version& version, const nmos::api_version& downgrade_version, const nmos::type& type, const web::json::value& pre, const web::json::value& post); + // insert 'value changed', 'sequence item added', 'sequence item changed' or 'sequence item removed' notification events into all grains whose subscriptions match the specified version, type and "pre" or "post" values + void insert_notification_events(nmos::resources& resources, const nmos::api_version& version, const nmos::api_version& downgrade_version, const nmos::type& type, const web::json::value& pre, const web::json::value& post, const web::json::value& event); + namespace fields { const web::json::field_as_string_or query_rql{ U("query.rql"), {} }; diff --git a/Development/nmos/type.h b/Development/nmos/type.h index 4fcf54f97..4e6831aa1 100644 --- a/Development/nmos/type.h +++ b/Development/nmos/type.h @@ -28,10 +28,13 @@ namespace nmos // to a subscription is managed as a sub-resource of the subscription const type grain{ U("grain") }; + // the Control Protocol API resource type, see nmos/control_protcol_resources.h + const type nc_object{ U("nc_object") }; + // all types ordered so that sub-resource types appear after super-resource types // according to the guidelines on referential integrity // see https://specs.amwa.tv/is-04/releases/v1.2.1/docs/4.1._Behaviour_-_Registration.html#referential-integrity - const std::vector all{ nmos::types::node, nmos::types::device, nmos::types::source, nmos::types::flow, nmos::types::sender, nmos::types::receiver, nmos::types::subscription, nmos::types::grain }; + const std::vector all{ nmos::types::node, nmos::types::device, nmos::types::source, nmos::types::flow, nmos::types::sender, nmos::types::receiver, nmos::types::subscription, nmos::types::grain, nmos::types::nc_object }; // the Channel Mapping API resource types, see nmos/channelmapping_resources.h const type input{ U("input") }; @@ -39,13 +42,6 @@ namespace nmos // the System API global configuration resource type, see nmos/system_resources.h const type global{ U("global") }; - - // the Control Protocol API resource types, see nmos/control_protcol_resources.h - const type nc_block{ U("nc_block") }; - const type nc_worker{ U("nc_worker") }; - const type nc_manager{ U("nc_manager") }; - const type nc_device_manager{ U("nc_device_manager") }; - const type nc_class_manager{ U("nc_class_manager") }; } } From b7a24e52dc448e8e0af00fcd198c6281f90cd03e Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 15 Sep 2023 15:35:15 +0100 Subject: [PATCH 045/250] Update outdated IS-12 links --- Development/nmos-cpp-node/config.json | 6 +- .../nmos/control_protocol_resource.cpp | 199 +++++++++-------- Development/nmos/control_protocol_resource.h | 202 +++++++++--------- .../nmos/control_protocol_resources.cpp | 4 +- Development/nmos/control_protocol_state.cpp | 4 +- Development/nmos/control_protocol_typedefs.h | 28 +-- Development/nmos/settings.h | 6 +- 7 files changed, 224 insertions(+), 225 deletions(-) diff --git a/Development/nmos-cpp-node/config.json b/Development/nmos-cpp-node/config.json index 397da858f..93ea3e669 100644 --- a/Development/nmos-cpp-node/config.json +++ b/Development/nmos-cpp-node/config.json @@ -288,17 +288,17 @@ //"ocsp_request_max": 30, // manufacturer_name [node]: the manufacturer name of the NcDeviceManager used for NMOS Control Protocol - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager //"manufacturer_name": "", // product_name/product_key/product_revision_level [node]: the product description of the NcDeviceManager used for NMOS Control Protocol - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncproduct + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncproduct //"product_name": "", //"product_key": "", //"product_revision_level": "", // serial_number [node]: the serial number of the NcDeviceManager used for NMOS Control Protocol - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager //"serial_number": "", "don't worry": "about trailing commas" diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 1a9f89507..74d934c68 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -31,7 +31,7 @@ namespace nmos return result; } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncelementid web::json::value make_nc_element_id(uint16_t level, uint16_t index) { using web::json::value_of; @@ -41,7 +41,6 @@ namespace nmos { nmos::fields::nc::index, index } }); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid web::json::value make_nc_element_id(const nc_element_id& id) { return make_nc_element_id(id.level, id.index); @@ -51,7 +50,7 @@ namespace nmos return { uint16_t(nmos::fields::nc::level(id)), uint16_t(nmos::fields::nc::index(id)) }; } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventid web::json::value make_nc_event_id(const nc_event_id& id) { return make_nc_element_id(id); @@ -61,7 +60,7 @@ namespace nmos return parse_nc_element_id(id); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethodid web::json::value make_nc_method_id(const nc_method_id& id) { return make_nc_element_id(id); @@ -71,7 +70,7 @@ namespace nmos return parse_nc_element_id(id); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertyid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyid web::json::value make_nc_property_id(const nc_property_id& id) { return make_nc_element_id(id); @@ -81,7 +80,7 @@ namespace nmos return parse_nc_element_id(id); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassid web::json::value make_nc_class_id(const nc_class_id& class_id) { using web::json::value; @@ -100,7 +99,7 @@ namespace nmos return class_id; } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanufacturer + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanufacturer web::json::value make_nc_manufacturer(const utility::string_t& name, const web::json::value& organization_id, const web::json::value& website) { using web::json::value_of; @@ -112,7 +111,7 @@ namespace nmos }); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncproduct + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncproduct // brand_name can be null // uuid can be null // description can be null @@ -131,7 +130,7 @@ namespace nmos }); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdeviceoperationalstate + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdeviceoperationalstate // device_specific_details can be null web::json::value make_nc_device_operational_state(nc_device_generic_state::state generic_state, const web::json::value& device_specific_details) { @@ -143,7 +142,7 @@ namespace nmos }); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdescriptor // description can be null web::json::value make_nc_descriptor(const web::json::value& description) { @@ -152,7 +151,7 @@ namespace nmos return value_of({ { nmos::fields::nc::description, description } }); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblockmemberdescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblockmemberdescriptor // description can be null // user_label can be null web::json::value make_nc_block_member_descriptor(const web::json::value& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const web::json::value& user_label, nc_oid owner) @@ -176,7 +175,7 @@ namespace nmos return make_nc_block_member_descriptor(value::string(description), role, oid, constant_oid, class_id, value::string(user_label), owner); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassdescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassdescriptor // description can be null // fixedRole can be null web::json::value make_nc_class_descriptor(const web::json::value& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) @@ -200,7 +199,7 @@ namespace nmos return make_nc_class_descriptor(value::string(description), class_id, name, fixed_role, properties, methods, events); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncenumitemdescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncenumitemdescriptor // description can be null web::json::value make_nc_enum_item_descriptor(const web::json::value& description, const nc_name& name, uint16_t val) { @@ -219,7 +218,7 @@ namespace nmos return make_nc_enum_item_descriptor(value::string(description), name, val); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventdescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventdescriptor // description can be null // id = make_nc_event_id(level, index) web::json::value make_nc_event_descriptor(const web::json::value& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated) @@ -241,7 +240,7 @@ namespace nmos return make_nc_event_descriptor(value::string(description), id, name, event_datatype, is_deprecated); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncfielddescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncfielddescriptor // description can be null // type_name can be null // constraints can be null @@ -265,7 +264,7 @@ namespace nmos return make_nc_field_descriptor(value::string(description), name, value::string(type_name), is_nullable, is_sequence, constraints); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethoddescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethoddescriptor // description can be null // id = make_nc_method_id(level, index) // sequence parameters @@ -289,7 +288,7 @@ namespace nmos return make_nc_method_descriptor(value::string(description), id, name, result_datatype, parameters, is_deprecated); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncparameterdescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterdescriptor // description can be null // type_name can be null web::json::value make_nc_parameter_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) @@ -318,7 +317,7 @@ namespace nmos return make_nc_parameter_descriptor(value::string(description), name, value::string(type_name), is_nullable, is_sequence, constraints); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertydescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertydescriptor // description can be null // constraints can be null web::json::value make_nc_property_descriptor(const web::json::value& description, const nc_property_id& id, const nc_name& name, const web::json::value& type_name, @@ -346,7 +345,7 @@ namespace nmos return nmos::details::make_nc_property_descriptor(value::string(description), id, name, value::string(type_name), is_read_only, is_nullable, is_sequence, is_deprecated, constraints); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptor // description can be null // constraints can be null web::json::value make_nc_datatype_descriptor(const web::json::value& description, const nc_name& name, nc_datatype_type::type type, const web::json::value& constraints) @@ -361,7 +360,7 @@ namespace nmos return data; } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorenum + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorenum // description can be null // constraints can be null // items: sequence @@ -373,7 +372,7 @@ namespace nmos return data; } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorprimitive + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorprimitive // description can be null // constraints can be null web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const nc_name& name, const web::json::value& constraints) @@ -381,7 +380,7 @@ namespace nmos return make_nc_datatype_descriptor(description, name, nc_datatype_type::Primitive, constraints); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorstruct + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorstruct // description can be null // constraints can be null // fields: sequence @@ -395,7 +394,7 @@ namespace nmos return data; } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptortypedef + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptortypedef // description can be null // constraints can be null web::json::value make_nc_datatype_typedef(const web::json::value& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints) @@ -409,7 +408,7 @@ namespace nmos return data; } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { using web::json::value; @@ -430,7 +429,7 @@ namespace nmos return data; } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members) { using web::json::value; @@ -442,7 +441,7 @@ namespace nmos return data; } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled) { using web::json::value; @@ -453,13 +452,13 @@ namespace nmos return data; } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { return make_nc_object(class_id, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause) @@ -481,7 +480,7 @@ namespace nmos return data; } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state) { using web::json::value; @@ -508,7 +507,7 @@ namespace nmos return data; } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertychangedeventdata + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertychangedeventdata web::json::value make_nc_property_changed_event_data(const nc_property_changed_event_data& property_changed_event_data) { using web::json::value_of; @@ -614,7 +613,7 @@ namespace nmos }); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject web::json::value make_nc_object_properties() { using web::json::value; @@ -690,7 +689,7 @@ namespace nmos return events; } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock web::json::value make_nc_block_properties() { using web::json::value; @@ -741,7 +740,7 @@ namespace nmos return value::array(); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker web::json::value make_nc_worker_properties() { using web::json::value; @@ -764,7 +763,7 @@ namespace nmos return value::array(); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager web::json::value make_nc_manager_properties() { using web::json::value; @@ -784,7 +783,7 @@ namespace nmos return value::array(); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager web::json::value make_nc_device_manager_properties() { using web::json::value; @@ -816,7 +815,7 @@ namespace nmos return value::array(); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager web::json::value make_nc_class_manager_properties() { using web::json::value; @@ -926,7 +925,7 @@ namespace nmos return value::array(); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.html web::json::value make_nc_object_class() { using web::json::value; @@ -934,7 +933,7 @@ namespace nmos return details::make_nc_class_descriptor(value::string(U("NcObject class descriptor")), nc_object_class_id, U("NcObject"), value::null(), make_nc_object_properties(), make_nc_object_methods(), make_nc_object_events()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.1.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.1.html web::json::value make_nc_block_class() { using web::json::value; @@ -942,7 +941,7 @@ namespace nmos return details::make_nc_class_descriptor(value::string(U("NcBlock class descriptor")), nc_block_class_id, U("NcBlock"), value::null(), make_nc_block_properties(), make_nc_block_methods(), make_nc_block_events()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.2.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.2.html web::json::value make_nc_worker_class() { using web::json::value; @@ -950,7 +949,7 @@ namespace nmos return details::make_nc_class_descriptor(value::string(U("NcWorker class descriptor")), nc_worker_class_id, U("NcWorker"), value::null(), make_nc_worker_properties(), make_nc_worker_methods(), make_nc_worker_events()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.html web::json::value make_nc_manager_class() { using web::json::value; @@ -958,7 +957,7 @@ namespace nmos return details::make_nc_class_descriptor(value::string(U("NcManager class descriptor")), nc_manager_class_id, U("NcManager"), value::null(), make_nc_manager_properties(), make_nc_manager_methods(), make_nc_manager_events()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.1.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.1.html web::json::value make_nc_device_manager_class() { using web::json::value; @@ -966,7 +965,7 @@ namespace nmos return details::make_nc_class_descriptor(value::string(U("NcDeviceManager class descriptor")), nc_device_manager_class_id, U("NcDeviceManager"), value::string(U("DeviceManager")), make_nc_device_manager_properties(), make_nc_device_manager_methods(), make_nc_device_manager_events()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.2.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.2.html web::json::value make_nc_class_manager_class() { using web::json::value; @@ -998,7 +997,7 @@ namespace nmos return details::make_nc_class_descriptor(value::string(U("NcReceiverMonitorProtected class descriptor")), nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), value::null(), make_nc_receiver_monitor_protected_properties(), make_nc_receiver_monitor_protected_methods(), make_nc_receiver_monitor_protected_events()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcBlockMemberDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcBlockMemberDescriptor.html web::json::value make_nc_block_member_descriptor_datatype() { using web::json::value; @@ -1013,7 +1012,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor which is specific to a block member")), U("NcBlockMemberDescriptor"), fields, value::string(U("NcDescriptor"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcClassDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassDescriptor.html web::json::value make_nc_class_descriptor_datatype() { using web::json::value; @@ -1028,7 +1027,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class")), U("NcClassDescriptor"), fields, value::string(U("NcDescriptor"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcClassId.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassId.html web::json::value make_nc_class_id_datatype() { using web::json::value; @@ -1036,7 +1035,7 @@ namespace nmos return details::make_nc_datatype_typedef(value::string(U("Sequence of class ID fields")), U("NcClassId"), true, U("NcInt32")); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptor.html web::json::value make_nc_datatype_descriptor_datatype() { using web::json::value; @@ -1048,7 +1047,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Base datatype descriptor")), U("NcDatatypeDescriptor"), fields, value::string(U("NcDescriptor"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorEnum.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorEnum.html web::json::value make_nc_datatype_descriptor_enum_datatype() { using web::json::value; @@ -1058,7 +1057,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Enum datatype descriptor")), U("NcDatatypeDescriptorEnum"), fields, value::string(U("NcDatatypeDescriptor"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorPrimitive.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorPrimitive.html web::json::value make_nc_datatype_descriptor_primitive_datatype() { using web::json::value; @@ -1067,7 +1066,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Primitive datatype descriptor")), U("NcDatatypeDescriptorPrimitive"), fields, value::string(U("NcDatatypeDescriptor"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorStruct.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorStruct.html web::json::value make_nc_datatype_descriptor_struct_datatype() { using web::json::value; @@ -1078,7 +1077,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Struct datatype descriptor")), U("NcDatatypeDescriptorStruct"), fields, value::string(U("NcDatatypeDescriptor"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorTypeDef.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorTypeDef.html web::json::value make_nc_datatype_descriptor_type_def_datatype() { using web::json::value; @@ -1089,7 +1088,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Type def datatype descriptor")), U("NcDatatypeDescriptorTypeDef"), fields, value::string(U("NcDatatypeDescriptor"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeType.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeType.html web::json::value make_nc_datatype_type_datatype() { using web::json::value; @@ -1102,7 +1101,7 @@ namespace nmos return details::make_nc_datatype_descriptor_enum(value::string(U("Datatype type")), U("NcDatatypeType"), items); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDescriptor.html web::json::value make_nc_descriptor_datatype() { using web::json::value; @@ -1112,7 +1111,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Base descriptor")), U("NcDescriptor"), fields, value::null()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDeviceGenericState.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceGenericState.html web::json::value make_nc_device_generic_state_datatype() { using web::json::value; @@ -1127,7 +1126,7 @@ namespace nmos return details::make_nc_datatype_descriptor_enum(value::string(U("Device generic operational state")), U("NcDeviceGenericState"), items); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDeviceOperationalState.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceOperationalState.html web::json::value make_nc_device_operational_state_datatype() { using web::json::value; @@ -1138,7 +1137,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Device operational state")), U("NcDeviceOperationalState"), fields, value::null()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcElementId.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcElementId.html web::json::value make_nc_element_id_datatype() { using web::json::value; @@ -1149,7 +1148,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Class element id which contains the level and index")), U("NcElementId"), fields, value::null()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEnumItemDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEnumItemDescriptor.html web::json::value make_nc_enum_item_descriptor_datatype() { using web::json::value; @@ -1160,7 +1159,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of an enum item")), U("NcEnumItemDescriptor"), fields, value::string(U("NcDescriptor"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEventDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventDescriptor.html web::json::value make_nc_event_descriptor_datatype() { using web::json::value; @@ -1173,7 +1172,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class event")), U("NcEventDescriptor"), fields, value::string(U("NcDescriptor"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEventId.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventId.html web::json::value make_nc_event_id_datatype() { using web::json::value; @@ -1181,7 +1180,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Event id which contains the level and index")), U("NcEventId"), value::array(), value::string(U("NcElementId"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcFieldDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcFieldDescriptor.html web::json::value make_nc_field_descriptor_datatype() { using web::json::value; @@ -1195,7 +1194,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a field of a struct")), U("NcFieldDescriptor"), fields, value::string(U("NcDescriptor"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcId.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcId.html web::json::value make_nc_id_datatype() { using web::json::value; @@ -1203,7 +1202,7 @@ namespace nmos return details::make_nc_datatype_typedef(value::string(U("Identity handler")), U("NcId"), false, U("NcUint32")); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcManufacturer.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcManufacturer.html web::json::value make_nc_manufacturer_datatype() { using web::json::value; @@ -1215,7 +1214,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Manufacturer descriptor")), U("NcManufacturer"), fields, value::null()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodDescriptor.html web::json::value make_nc_method_descriptor_datatype() { using web::json::value; @@ -1229,7 +1228,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class method")), U("NcMethodDescriptor"), fields, value::string(U("NcDescriptor"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodId.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodId.html web::json::value make_nc_method_id_datatype() { using web::json::value; @@ -1237,7 +1236,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Method id which contains the level and index")), U("NcMethodId"), value::array(), value::string(U("NcElementId"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResult.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResult.html web::json::value make_nc_method_result_datatype() { using web::json::value; @@ -1247,7 +1246,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Base result of the invoked method")), U("NcMethodResult"), fields, value::null()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultBlockMemberDescriptors.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultBlockMemberDescriptors.html web::json::value make_nc_method_result_block_member_descriptors_datatype() { using web::json::value; @@ -1257,7 +1256,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing block member descriptors as the value")), U("NcMethodResultBlockMemberDescriptors"), fields, value::string(U("NcMethodResult"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultClassDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultClassDescriptor.html web::json::value make_nc_method_result_class_descriptor_datatype() { using web::json::value; @@ -1267,7 +1266,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing a class descriptor as the value")), U("NcMethodResultClassDescriptor"), fields, value::string(U("NcMethodResult"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultDatatypeDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultDatatypeDescriptor.html web::json::value make_nc_method_result_datatype_descriptor_datatype() { using web::json::value; @@ -1277,7 +1276,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing a datatype descriptor as the value")), U("NcMethodResultDatatypeDescriptor"), fields, value::string(U("NcMethodResult"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultError.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultError.html web::json::value make_nc_method_result_error_datatype() { using web::json::value; @@ -1287,7 +1286,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Error result - to be used when the method call encounters an error")), U("NcMethodResultError"), fields, value::string(U("NcMethodResult"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultId.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultId.html web::json::value make_nc_method_result_id_datatype() { using web::json::value; @@ -1297,7 +1296,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Id method result")), U("NcMethodResultId"), fields, value::string(U("NcMethodResult"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultLength.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultLength.html web::json::value make_nc_method_result_length_datatype() { using web::json::value; @@ -1307,7 +1306,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Length method result")), U("NcMethodResultLength"), fields, value::string(U("NcMethodResult"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultPropertyValue.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultPropertyValue.html web::json::value make_nc_method_result_property_value_datatype() { using web::json::value; @@ -1317,7 +1316,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Result when invoking the getter method associated with a property")), U("NcMethodResultPropertyValue"), fields, value::string(U("NcMethodResult"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodStatus.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodStatus.html web::json::value make_nc_method_status_datatype() { using web::json::value; @@ -1344,7 +1343,7 @@ namespace nmos return details::make_nc_datatype_descriptor_enum(value::string(U("Method invokation status")), U("NcMethodStatus"), items); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcName.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcName.html web::json::value make_nc_name_datatype() { using web::json::value; @@ -1352,7 +1351,7 @@ namespace nmos return details::make_nc_datatype_typedef(value::string(U("Programmatically significant name, alphanumerics + underscore, no spaces")), U("NcName"), false, U("NcString")); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcOid.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOid.html web::json::value make_nc_oid_datatype() { using web::json::value; @@ -1360,7 +1359,7 @@ namespace nmos return details::make_nc_datatype_typedef(value::string(U("Object id")), U("NcOid"), false, U("NcUint32")); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcOrganizationId.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOrganizationId.html web::json::value make_nc_organization_id_datatype() { using web::json::value; @@ -1368,7 +1367,7 @@ namespace nmos return details::make_nc_datatype_typedef(value::string(U("Unique 24-bit organization id")), U("NcOrganizationId"), false, U("NcInt32")); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraints.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraints.html web::json::value make_nc_parameter_constraints_datatype() { using web::json::value; @@ -1378,7 +1377,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Abstract parameter constraints class")), U("NcParameterConstraints"), fields, value::null()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraintsNumber.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsNumber.html web::json::value make_nc_parameter_constraints_number_datatype() { using web::json::value; @@ -1390,7 +1389,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Number parameter constraints class")), U("NcParameterConstraintsNumber"), fields, value::string(U("NcParameterConstraints"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraintsString.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsString.html web::json::value make_nc_parameter_constraints_string_datatype() { using web::json::value; @@ -1401,7 +1400,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("String parameter constraints class")), U("NcParameterConstraintsString"), fields, value::string(U("NcParameterConstraints"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterDescriptor.html web::json::value make_nc_parameter_descriptor_datatype() { using web::json::value; @@ -1415,7 +1414,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a method parameter")), U("NcParameterDescriptor"), fields, value::string(U("NcDescriptor"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcProduct.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcProduct.html web::json::value make_nc_product_datatype() { using web::json::value; @@ -1430,7 +1429,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Product descriptor")), U("NcProduct"), fields, value::null()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyChangeType.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangeType.html web::json::value make_nc_property_change_type_datatype() { using web::json::value; @@ -1443,7 +1442,7 @@ namespace nmos return details::make_nc_datatype_descriptor_enum(value::string(U("Type of property change")), U("NcPropertyChangeType"), items); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyChangedEventData.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangedEventData.html web::json::value make_nc_property_changed_event_data_datatype() { using web::json::value; @@ -1456,7 +1455,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Payload of property-changed event")), U("NcPropertyChangedEventData"), fields, value::null()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraints.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraints.html web::json::value make_nc_property_contraints_datatype() { using web::json::value; @@ -1467,7 +1466,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Property constraints class")), U("NcPropertyConstraints"), fields, value::null()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraintsNumber.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsNumber.html web::json::value make_nc_property_constraints_number_datatype() { using web::json::value; @@ -1479,7 +1478,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Number property constraints class")), U("NcPropertyConstraintsNumber"), fields, value::string(U("NcPropertyConstraints"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraintsString.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsString.html web::json::value make_nc_property_constraints_string_datatype() { using web::json::value; @@ -1490,7 +1489,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("String property constraints class")), U("NcPropertyConstraintsString"), fields, value::string(U("NcPropertyConstraints"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyDescriptor.html web::json::value make_nc_property_descriptor_datatype() { using web::json::value; @@ -1507,7 +1506,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class property")), U("NcPropertyDescriptor"), fields, value::string(U("NcDescriptor"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyId.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyId.html web::json::value make_nc_property_id_datatype() { using web::json::value; @@ -1515,7 +1514,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Property id which contains the level and index")), U("NcPropertyId"), value::array(), value::string(U("NcElementId"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcRegex.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRegex.html web::json::value make_nc_regex_datatype() { using web::json::value; @@ -1523,7 +1522,7 @@ namespace nmos return details::make_nc_datatype_typedef(value::string(U("Regex pattern")), U("NcRegex"), false, U("NcString")); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcResetCause.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcResetCause.html web::json::value make_nc_reset_cause_datatype() { using web::json::value; @@ -1538,7 +1537,7 @@ namespace nmos return details::make_nc_datatype_descriptor_enum(value::string(U("Reset cause enum")), U("NcResetCause"), items); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcRolePath.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRolePath.html web::json::value make_nc_role_path_datatype() { using web::json::value; @@ -1546,7 +1545,7 @@ namespace nmos return details::make_nc_datatype_typedef(value::string(U("Role path")), U("NcRolePath"), true, U("NcString")); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTimeInterval.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTimeInterval.html web::json::value make_nc_time_interval_datatype() { using web::json::value; @@ -1554,7 +1553,7 @@ namespace nmos return details::make_nc_datatype_typedef(value::string(U("Time interval described in nanoseconds")), U("NcTimeInterval"), false, U("NcInt64")); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpoint.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpoint.html web::json::value make_nc_touchpoint_datatype() { using web::json::value; @@ -1564,7 +1563,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Base touchpoint class")), U("NcTouchpoint"), fields, value::null()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointNmos.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmos.html web::json::value make_nc_touchpoint_nmos_datatype() { using web::json::value; @@ -1574,7 +1573,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Touchpoint class for NMOS resources")), U("NcTouchpointNmos"), fields, value::string(U("NcTouchpoint"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointNmosChannelMapping.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmosChannelMapping.html web::json::value make_nc_touchpoint_nmos_channel_mapping_datatype() { using web::json::value; @@ -1584,7 +1583,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Touchpoint class for NMOS IS-08 resources")), U("NcTouchpointNmosChannelMapping"), fields, value::string(U("NcTouchpoint"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResource.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResource.html web::json::value make_nc_touchpoint_resource_datatype() { using web::json::value; @@ -1594,7 +1593,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Touchpoint resource class")), U("NcTouchpointResource"), fields, value::null()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResourceNmos.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmos.html web::json::value make_nc_touchpoint_resource_nmos_datatype() { using web::json::value; @@ -1604,7 +1603,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Touchpoint resource class for NMOS resources")), U("NcTouchpointResourceNmos"), fields, value::string(U("NcTouchpointResource"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResourceNmosChannelMapping.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmosChannelMapping.html web::json::value make_nc_touchpoint_resource_nmos_channel_mapping_datatype() { using web::json::value; @@ -1614,7 +1613,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(value::string(U("Touchpoint resource class for NMOS resources")), U("NcTouchpointResourceNmosChannelMapping"), fields, value::string(U("NcTouchpointResourceNmos"))); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcUri.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUri.html web::json::value make_nc_uri_datatype() { using web::json::value; @@ -1622,7 +1621,7 @@ namespace nmos return details::make_nc_datatype_typedef(value::string(U("Uniform resource identifier")), U("NcUri"), false, U("NcString")); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcUuid.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUuid.html web::json::value make_nc_uuid_datatype() { using web::json::value; @@ -1630,7 +1629,7 @@ namespace nmos return details::make_nc_datatype_typedef(value::string(U("UUID")), U("NcUuid"), false, U("NcString")); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcVersionCode.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcVersionCode.html web::json::value make_nc_version_code_datatype() { using web::json::value; diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index f40234977..bff521676 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -21,91 +21,91 @@ namespace nmos namespace details { - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncelementid //web::json::value make_nc_element_id(uint16_t level, uint16_t index); web::json::value make_nc_element_id(const nc_element_id& element_id); nc_element_id parse_nc_element_id(const web::json::value& element_id); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventid //web::json::value make_nc_event_id(uint16_t level, uint16_t index); web::json::value make_nc_event_id(const nc_event_id& event_id); nc_event_id parse_nc_event_id(const web::json::value& event_id); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethodid web::json::value make_nc_method_id(const nc_method_id& method_id); nc_method_id parse_nc_method_id(const web::json::value& method_id); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertyid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyid web::json::value make_nc_property_id(const nc_property_id& property_id); nc_property_id parse_nc_property_id(const web::json::value& property_id); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassid web::json::value make_nc_class_id(const nc_class_id& class_id); nc_class_id parse_nc_class_id(const web::json::array& class_id); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanufacturer + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanufacturer web::json::value make_nc_manufacturer(const utility::string_t& name, const web::json::value& organization_id = web::json::value::null(), const web::json::value& website = web::json::value::null()); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncproduct + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncproduct // brand_name can be null // uuid can be null // description can be null web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, const web::json::value& brand_name = web::json::value::null(), const web::json::value& uuid = web::json::value::null(), const web::json::value& description = web::json::value::null()); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdeviceoperationalstate + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdeviceoperationalstate // device_specific_details can be null web::json::value make_nc_device_operational_state(nc_device_generic_state::state generic_state, const web::json::value& device_specific_details); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdescriptor // description can be null web::json::value make_nc_descriptor(const web::json::value& description); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblockmemberdescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblockmemberdescriptor // description can be null // user_label can be null web::json::value make_nc_block_member_descriptor(const web::json::value& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const web::json::value& user_label, nc_oid owner); web::json::value make_nc_block_member_descriptor(const utility::string_t& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const utility::string_t& user_label, nc_oid owner); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassdescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassdescriptor // description can be null // fixedRole can be null web::json::value make_nc_class_descriptor(const web::json::value& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncenumitemdescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncenumitemdescriptor // description can be null web::json::value make_nc_enum_item_descriptor(const web::json::value& description, const nc_name& name, uint16_t val); web::json::value make_nc_enum_item_descriptor(const utility::string_t& description, const nc_name& name, uint16_t val); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventdescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventdescriptor // description can be null // id = make_nc_event_id(level, index) web::json::value make_nc_event_descriptor(const web::json::value& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated); web::json::value make_nc_event_descriptor(const utility::string_t& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncfielddescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncfielddescriptor // description can be null // type_name can be null // constraints can be null web::json::value make_nc_field_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethoddescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethoddescriptor // description can be null // id = make_nc_method_id(level, index) // sequence parameters web::json::value make_nc_method_descriptor(const web::json::value& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated); web::json::value make_nc_method_descriptor(const utility::string_t& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncparameterdescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterdescriptor // description can be null // type_name can be null web::json::value make_nc_parameter_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertydescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertydescriptor // description can be null // id = make_nc_property_id(level, index); // type_name can be null @@ -115,52 +115,52 @@ namespace nmos web::json::value make_nc_property_descriptor(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints = web::json::value::null()); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptor // description can be null // constraints can be null web::json::value make_nc_datatype_descriptor(const web::json::value& description, const nc_name& name, nc_datatype_type::type type, const web::json::value& constraints = web::json::value::null()); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorenum + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorenum // description can be null // constraints can be null // items: sequence web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints = web::json::value::null()); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorprimitive + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorprimitive // description can be null // constraints can be null web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const nc_name& name, const web::json::value& constraints = web::json::value::null()); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptorstruct + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorstruct // description can be null // constraints can be null // fields: sequence // parent_type can be null web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const nc_name& name, const web::json::value& fields, const web::json::value& parent_type, const web::json::value& constraints = web::json::value::null()); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdatatypedescriptortypedef + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptortypedef // description can be null // constraints can be null web::json::value make_nc_datatype_typedef(const web::json::value& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints = web::json::value::null()); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state); } @@ -182,23 +182,23 @@ namespace nmos web::json::value make_control_protocol_notification(const web::json::value& notifications); // error message - // See https://specs.amwa.tv/is-12/branches/v1.0-dev/docs/Protocol_messaging.html#error-messages + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#error-messages web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message); // Control class models - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/#control-class-models-for-branch-v10-dev + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/#control-class-models-for-branch-v10-dev // - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.html web::json::value make_nc_object_class(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.1.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.1.html web::json::value make_nc_block_class(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.2.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.2.html web::json::value make_nc_worker_class(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.html web::json::value make_nc_manager_class(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.1.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.1.html web::json::value make_nc_device_manager_class(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/1.3.2.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.2.html web::json::value make_nc_class_manager_class(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon web::json::value make_nc_ident_beacon_class(); @@ -208,27 +208,27 @@ namespace nmos web::json::value make_nc_receiver_monitor_protected_class(); // control classes proprties/methods/events - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncobject + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject web::json::value make_nc_object_properties(); web::json::value make_nc_object_methods(); web::json::value make_nc_object_events(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncblock + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock web::json::value make_nc_block_properties(); web::json::value make_nc_block_methods(); web::json::value make_nc_block_events(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncworker + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker web::json::value make_nc_worker_properties(); web::json::value make_nc_worker_methods(); web::json::value make_nc_worker_events(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager web::json::value make_nc_manager_properties(); web::json::value make_nc_manager_methods(); web::json::value make_nc_manager_events(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager web::json::value make_nc_device_manager_properties(); web::json::value make_nc_device_manager_methods(); web::json::value make_nc_device_manager_events(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager web::json::value make_nc_class_manager_properties(); web::json::value make_nc_class_manager_methods(); web::json::value make_nc_class_manager_events(); @@ -246,123 +246,123 @@ namespace nmos web::json::value make_nc_ident_beacon_events(); // Datatype models - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/#datatype-models-for-branch-v10-dev + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/#datatype-models-for-branch-v10-dev // - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcBlockMemberDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcBlockMemberDescriptor.html web::json::value make_nc_block_member_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcClassDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassDescriptor.html web::json::value make_nc_class_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcClassId.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassId.html web::json::value make_nc_class_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptor.html web::json::value make_nc_datatype_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorEnum.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorEnum.html web::json::value make_nc_datatype_descriptor_enum_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorPrimitive.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorPrimitive.html web::json::value make_nc_datatype_descriptor_primitive_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorStruct.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorStruct.html web::json::value make_nc_datatype_descriptor_struct_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeDescriptorTypeDef.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorTypeDef.html web::json::value make_nc_datatype_descriptor_type_def_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDatatypeType.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeType.html web::json::value make_nc_datatype_type_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDescriptor.html web::json::value make_nc_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDeviceGenericState.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceGenericState.html web::json::value make_nc_device_generic_state_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcDeviceOperationalState.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceOperationalState.html web::json::value make_nc_device_operational_state_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcElementId.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcElementId.html web::json::value make_nc_element_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEnumItemDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEnumItemDescriptor.html web::json::value make_nc_enum_item_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEventDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventDescriptor.html web::json::value make_nc_event_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcEventId.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventId.html web::json::value make_nc_event_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcFieldDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcFieldDescriptor.html web::json::value make_nc_field_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcId.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcId.html web::json::value make_nc_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcManufacturer.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcManufacturer.html web::json::value make_nc_manufacturer_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodDescriptor.html web::json::value make_nc_method_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodId.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodId.html web::json::value make_nc_method_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResult.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResult.html web::json::value make_nc_method_result_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultBlockMemberDescriptors.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultBlockMemberDescriptors.html web::json::value make_nc_method_result_block_member_descriptors_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultClassDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultClassDescriptor.html web::json::value make_nc_method_result_class_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultDatatypeDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultDatatypeDescriptor.html web::json::value make_nc_method_result_datatype_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultError.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultError.html web::json::value make_nc_method_result_error_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultId.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultId.html web::json::value make_nc_method_result_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultLength.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultLength.html web::json::value make_nc_method_result_length_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodResultPropertyValue.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultPropertyValue.html web::json::value make_nc_method_result_property_value_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcMethodStatus.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodStatus.html web::json::value make_nc_method_status_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcName.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcName.html web::json::value make_nc_name_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcOid.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOid.html web::json::value make_nc_oid_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcOrganizationId.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOrganizationId.html web::json::value make_nc_organization_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraints.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraints.html web::json::value make_nc_parameter_constraints_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraintsNumber.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsNumber.html web::json::value make_nc_parameter_constraints_number_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterConstraintsString.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsString.html web::json::value make_nc_parameter_constraints_string_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcParameterDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterDescriptor.html web::json::value make_nc_parameter_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcProduct.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcProduct.html web::json::value make_nc_product_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyChangeType.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangeType.html web::json::value make_nc_property_change_type_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyChangedEventData.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangedEventData.html web::json::value make_nc_property_changed_event_data_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraints.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraints.html web::json::value make_nc_property_contraints_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraintsNumber.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsNumber.html web::json::value make_nc_property_constraints_number_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyConstraintsString.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsString.html web::json::value make_nc_property_constraints_string_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyDescriptor.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyDescriptor.html web::json::value make_nc_property_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcPropertyId.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyId.html web::json::value make_nc_property_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcRegex.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRegex.html web::json::value make_nc_regex_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcResetCause.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcResetCause.html web::json::value make_nc_reset_cause_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcRolePath.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRolePath.html web::json::value make_nc_role_path_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTimeInterval.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTimeInterval.html web::json::value make_nc_time_interval_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpoint.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpoint.html web::json::value make_nc_touchpoint_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointNmos.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmos.html web::json::value make_nc_touchpoint_nmos_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointNmosChannelMapping.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmosChannelMapping.html web::json::value make_nc_touchpoint_nmos_channel_mapping_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResource.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResource.html web::json::value make_nc_touchpoint_resource_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResourceNmos.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmos.html web::json::value make_nc_touchpoint_resource_nmos_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcTouchpointResourceNmosChannelMapping.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmosChannelMapping.html web::json::value make_nc_touchpoint_resource_nmos_channel_mapping_datatype(); - // See // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcUri.html + // See // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUri.html web::json::value make_nc_uri_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcUuid.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUuid.html web::json::value make_nc_uuid_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/NcVersionCode.html + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcVersionCode.html web::json::value make_nc_version_code_datatype(); // Monitoring datatypes diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index 48b6e7d1f..b7e28fd29 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -37,7 +37,7 @@ namespace nmos return details::make_block(1, value::null(), U("root"), U("Root"), value::null(), value::null(), value::array()); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager resource make_device_manager(nc_oid oid, const nmos::settings& settings) { using web::json::value; @@ -56,7 +56,7 @@ namespace nmos return{ is12_versions::v1_0, types::nc_object, std::move(data), true }; } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassmanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager resource make_class_manager(nc_oid oid, const nmos::experimental::control_protocol_state& control_protocol_state) { using web::json::value; diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index c308ad26d..f9a24eb65 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -116,7 +116,7 @@ namespace nmos control_classes = { // Control class models - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/classes/#control-class-models-for-branch-v10-dev + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/#control-class-models-for-branch-v10-dev { nc_object_class_id, make_control_class(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), to_vector(make_nc_object_properties()), to_methods_vector(make_nc_object_methods(), { @@ -161,7 +161,7 @@ namespace nmos datatypes = { // Dataype models - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/models/datatypes/#datatype-models-for-branch-v10-dev + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/#datatype-models-for-branch-v10-dev { U("NcClassId"), {make_nc_class_id_datatype()} }, { U("NcOid"), {make_nc_oid_datatype()} }, { U("NcTouchpoint"), {make_nc_touchpoint_datatype()} }, diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index 9c0962742..92e655df2 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -46,7 +46,7 @@ namespace nmos }; } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodresult + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethodresult struct nc_method_result { nc_method_status::status status; @@ -119,7 +119,7 @@ namespace nmos } // NcElementId - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncelementid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncelementid struct nc_element_id { uint16_t level; @@ -137,14 +137,14 @@ namespace nmos }; // NcEventId - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nceventid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventid typedef nc_element_id nc_event_id; // NcEventIds for NcObject // SEe https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject const nc_event_id nc_object_property_changed_event_id(1, 1); // NcMethodId - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncmethodid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethodid typedef nc_element_id nc_method_id; // NcMethodIds for NcObject // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject @@ -167,7 +167,7 @@ namespace nmos const nc_method_id nc_class_manager_get_datatype_method_id(3, 2); // NcPropertyId - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertyid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyid typedef nc_element_id nc_property_id; // NcPropertyIds for NcObject // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject @@ -216,29 +216,29 @@ namespace nmos const nc_property_id nc_ident_beacon_active_property_id(3, 1); // NcId - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncid typedef uint32_t nc_id; // NcName - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncname + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncname typedef utility::string_t nc_name; // NcOid - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncoid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncoid typedef uint32_t nc_oid; // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Blocks.html const nc_oid root_block_oid{ 1 }; // NcUri - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncuri + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncuri typedef utility::string_t nc_uri; // NcUuid - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncuuid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncuuid typedef utility::string_t nc_uuid; // NcClassId - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncclassid + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassid typedef std::vector nc_class_id; // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject const nc_class_id nc_object_class_id({ 1 }); @@ -260,11 +260,11 @@ namespace nmos const nc_class_id nc_receiver_monitor_protected_class_id({ 1, 2, 3, 1 }); // NcTouchpoint - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#nctouchpoint + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpoint typedef utility::string_t nc_touch_point; // NcPropertyChangeType - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertychangetype + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertychangetype namespace nc_property_change_type { enum type @@ -277,7 +277,7 @@ namespace nmos } // NcPropertyChangedEventData - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncpropertychangedeventdata + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertychangedeventdata struct nc_property_changed_event_data { nc_property_id property_id; diff --git a/Development/nmos/settings.h b/Development/nmos/settings.h index 467c7c0e2..94c9d6f7a 100644 --- a/Development/nmos/settings.h +++ b/Development/nmos/settings.h @@ -366,17 +366,17 @@ namespace nmos const web::json::field_as_integer_or ocsp_request_max{ U("ocsp_request_max"), 30 }; // manufacturer_name [node]: the manufacturer name of the NcDeviceManager used for NMOS Control Protocol - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager const web::json::field_as_string_or manufacturer_name{ U("manufacturer_name"), U("") }; // product_name/product_key/product_revision_level [node]: the product description of the NcDeviceManager used for NMOS Control Protocol - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncproduct + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncproduct const web::json::field_as_string_or product_name{ U("product_name"), U("") }; const web::json::field_as_string_or product_key{ U("product_key"), U("") }; const web::json::field_as_string_or product_revision_level{ U("product_revision_level"), U("") }; // serial_number [node]: the serial number of the NcDeviceManager used for NMOS Control Protocol - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager const web::json::field_as_string_or serial_number{ U("serial_number"), U("") }; } } From ec516384c9d818b642650c1aa238f531a0f496db Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 19 Sep 2023 00:12:12 +0100 Subject: [PATCH 046/250] Tidy up, less casting --- .../nmos-cpp-node/node_implementation.cpp | 20 +- .../nmos/control_protocol_resource.cpp | 518 ++++++++++-------- Development/nmos/control_protocol_resource.h | 30 +- 3 files changed, 305 insertions(+), 263 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 8039782f9..8d3588c6e 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1025,28 +1025,28 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr using web::json::value; auto items = value::array(); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(value::string(U("Undefined")), U("Undefined"), example_enum::Undefined)); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(value::string(U("Alpha")), U("Alpha"), example_enum::Alpha)); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(value::string(U("Beta")), U("Beta"), example_enum::Beta)); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(value::string(U("Gamma")), U("Gamma"), example_enum::Gamma)); - return nmos::details::make_nc_datatype_descriptor_enum(value::string(U("Example enum datatype")), U("ExampleEnum"), items); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Undefined"), U("Undefined"), example_enum::Undefined)); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Alpha"), U("Alpha"), example_enum::Alpha)); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Beta"), U("Beta"), example_enum::Beta)); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Gamma"), U("Gamma"), example_enum::Gamma)); + return nmos::details::make_nc_datatype_descriptor_enum(U("Example enum datatype"), U("ExampleEnum"), items, value::null()); }; auto make_example_datatype_datatype = [&]() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(value::string(U("Enum property example")), enum_property, value::string(U("ExampleEnum")), false, false)); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Enum property example"), enum_property, U("ExampleEnum"), false, false, value::null())); { value constraints = value::null(); // todo constraints - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(value::string(U("String property example")), string_property, value::string(U("NcString")), false, false, constraints)); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("String property example"), string_property, U("NcString"), false, false, constraints)); } { value constraints = value::null(); // todo constraints - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(value::string(U("Number property example")), number_property, value::string(U("NcUint64")), false, false, constraints)); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Number property example"), number_property, U("NcUint64"), false, false, constraints)); } - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(value::string(U("Boolean property example")), boolean_property, value::string(U("NcBoolean")), false, false)); - return nmos::details::make_nc_datatype_descriptor_struct(value::string(U("Example data type")), U("ExampleDataType"), fields, value::null()); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Boolean property example"), boolean_property, U("NcBoolean"), false, false, value::null())); + return nmos::details::make_nc_datatype_descriptor_struct(U("Example data type"), U("ExampleDataType"), fields, value::null()); }; control_protocol_state.insert(nmos::experimental::datatype{ make_example_enum_datatype() }); control_protocol_state.insert(nmos::experimental::datatype{ make_example_datatype_datatype() }); diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 74d934c68..8d89358b2 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -263,6 +263,12 @@ namespace nmos return make_nc_field_descriptor(value::string(description), name, value::string(type_name), is_nullable, is_sequence, constraints); } + web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + { + using web::json::value; + + return make_nc_field_descriptor(value::string(description), name, value::null(), is_nullable, is_sequence, constraints); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethoddescriptor // description can be null @@ -371,6 +377,12 @@ namespace nmos return data; } + web::json::value make_nc_datatype_descriptor_enum(const utility::string_t& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints) + { + using web::json::value; + + return make_nc_datatype_descriptor_enum(value::string(description), name, items, constraints); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorprimitive // description can be null @@ -379,6 +391,12 @@ namespace nmos { return make_nc_datatype_descriptor(description, name, nc_datatype_type::Primitive, constraints); } + web::json::value make_nc_datatype_descriptor_primitive(const utility::string_t& description, const nc_name& name, const web::json::value& constraints) + { + using web::json::value; + + return make_nc_datatype_descriptor_primitive(value::string(description), name, constraints); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorstruct // description can be null @@ -393,6 +411,18 @@ namespace nmos return data; } + web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const utility::string_t& parent_type, const web::json::value& constraints) + { + using web::json::value; + + return make_nc_datatype_descriptor_struct(value::string(description), name, fields, value::string(parent_type), constraints); + } + web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const web::json::value& constraints) + { + using web::json::value; + + return make_nc_datatype_descriptor_struct(value::string(description), name, fields, value::null(), constraints); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptortypedef // description can be null @@ -407,6 +437,12 @@ namespace nmos return data; } + web::json::value make_nc_datatype_typedef(const utility::string_t& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints) + { + using web::json::value; + + return make_nc_datatype_typedef(value::string(description), name, is_sequence, parent_type, constraints); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) @@ -619,14 +655,14 @@ namespace nmos using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Static value. All instances of the same class will have the same identity value"), nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Object identifier"), nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE iff OID is hardwired into device"), nc_object_constant_oid_property_id, nmos::fields::nc::constant_oid, U("NcBoolean"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("OID of containing block. Can only ever be null for the root block"), nc_object_owner_property_id, nmos::fields::nc::owner, U("NcOid"), true, true, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Role of object in the containing block"), nc_object_role_property_id, nmos::fields::nc::role, U("NcString"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Scribble strip"), nc_object_user_label_property_id, nmos::fields::nc::user_label, U("NcString"), false, true, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Touchpoints to other contexts"), nc_object_touchpoints_property_id, nmos::fields::nc::touchpoints, U("NcTouchpoint"), true, true, true, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Runtime property constraints"), nc_object_runtime_property_constraints_property_id, nmos::fields::nc::runtime_property_constraints, U("NcPropertyConstraints"), true, true, true, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Static value. All instances of the same class will have the same identity value"), nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Object identifier"), nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE iff OID is hardwired into device"), nc_object_constant_oid_property_id, nmos::fields::nc::constant_oid, U("NcBoolean"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("OID of containing block. Can only ever be null for the root block"), nc_object_owner_property_id, nmos::fields::nc::owner, U("NcOid"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Role of object in the containing block"), nc_object_role_property_id, nmos::fields::nc::role, U("NcString"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Scribble strip"), nc_object_user_label_property_id, nmos::fields::nc::user_label, U("NcString"), false, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Touchpoints to other contexts"), nc_object_touchpoints_property_id, nmos::fields::nc::touchpoints, U("NcTouchpoint"), true, true, true, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Runtime property constraints"), nc_object_runtime_property_constraints_property_id, nmos::fields::nc::runtime_property_constraints, U("NcPropertyConstraints"), true, true, true, false, value::null())); return properties; } @@ -643,7 +679,7 @@ namespace nmos { auto parameters = value::array(); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property value"), nmos::fields::nc::value, true, false)); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); web::json::push_back(methods, details::make_nc_method_descriptor(U("Set property value"), nc_object_set_method_id, U("Set"), U("NcMethodResult"), parameters, false)); } { @@ -656,13 +692,13 @@ namespace nmos auto parameters = value::array(); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false)); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); web::json::push_back(methods, details::make_nc_method_descriptor(U("Set sequence item value"), nc_object_set_sequence_item_method_id, U("SetSequenceItem"), U("NcMethodResult"), parameters, false)); } { auto parameters = value::array(); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id,U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false)); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); web::json::push_back(methods, details::make_nc_method_descriptor(U("Add item to sequence"), nc_object_add_sequence_item_method_id, U("AddSequenceItem"), U("NcMethodResultId"), parameters, false)); } { @@ -695,8 +731,8 @@ namespace nmos using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE if block is functional"), nc_block_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptors of this block's members"), nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE if block is functional"), nc_block_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptors of this block's members"), nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, value::null())); return properties; } @@ -746,7 +782,7 @@ namespace nmos using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE iff worker is enabled"), nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE iff worker is enabled"), nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, value::null())); return properties; } @@ -789,16 +825,16 @@ namespace nmos using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Version of MS-05-02 that this device uses"), nc_device_manager_nc_version_property_id, nmos::fields::nc::nc_version, U("NcVersionCode"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Manufacturer descriptor"), nc_device_manager_manufacturer_property_id, nmos::fields::nc::manufacturer, U("NcManufacturer"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Product descriptor"), nc_device_manager_product_property_id, nmos::fields::nc::product, U("NcProduct"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Serial number"), nc_device_manager_serial_number_property_id, nmos::fields::nc::serial_number, U("NcString"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Asset tracking identifier (user specified)"), nc_device_manager_user_inventory_code_property_id, nmos::fields::nc::user_inventory_code, U("NcString"), false, true, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Name of this device in the application. Instance name, not product name"), nc_device_manager_device_name_property_id, nmos::fields::nc::device_name, U("NcString"), false, true, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Role of this device in the application"), nc_device_manager_device_role_property_id, nmos::fields::nc::device_role, U("NcString"), false, true, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Device operational state"), nc_device_manager_operational_state_property_id, nmos::fields::nc::operational_state, U("NcDeviceOperationalState"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Reason for most recent reset"), nc_device_manager_reset_cause_property_id, nmos::fields::nc::reset_cause, U("NcResetCause"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Arbitrary message from dev to controller"), nc_device_manager_message_property_id, nmos::fields::nc::message, U("NcString"), true, true, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Version of MS-05-02 that this device uses"), nc_device_manager_nc_version_property_id, nmos::fields::nc::nc_version, U("NcVersionCode"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Manufacturer descriptor"), nc_device_manager_manufacturer_property_id, nmos::fields::nc::manufacturer, U("NcManufacturer"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Product descriptor"), nc_device_manager_product_property_id, nmos::fields::nc::product, U("NcProduct"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Serial number"), nc_device_manager_serial_number_property_id, nmos::fields::nc::serial_number, U("NcString"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Asset tracking identifier (user specified)"), nc_device_manager_user_inventory_code_property_id, nmos::fields::nc::user_inventory_code, U("NcString"), false, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Name of this device in the application. Instance name, not product name"), nc_device_manager_device_name_property_id, nmos::fields::nc::device_name, U("NcString"), false, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Role of this device in the application"), nc_device_manager_device_role_property_id, nmos::fields::nc::device_role, U("NcString"), false, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Device operational state"), nc_device_manager_operational_state_property_id, nmos::fields::nc::operational_state, U("NcDeviceOperationalState"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Reason for most recent reset"), nc_device_manager_reset_cause_property_id, nmos::fields::nc::reset_cause, U("NcResetCause"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Arbitrary message from dev to controller"), nc_device_manager_message_property_id, nmos::fields::nc::message, U("NcString"), true, true, false, false, value::null())); return properties; } @@ -821,8 +857,8 @@ namespace nmos using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptions of all control classes in the device (descriptors do not contain inherited elements)"), nc_class_manager_control_classes_property_id, nmos::fields::nc::control_classes, U("NcClassDescriptor"), true, false, true, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptions of all data types in the device (descriptors do not contain inherited elements)"), nc_class_manager_datatypes_property_id, nmos::fields::nc::datatypes, U("NcDatatypeDescriptor"), true, false, true, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptions of all control classes in the device (descriptors do not contain inherited elements)"), nc_class_manager_control_classes_property_id, nmos::fields::nc::control_classes, U("NcClassDescriptor"), true, false, true, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptions of all data types in the device (descriptors do not contain inherited elements)"), nc_class_manager_datatypes_property_id, nmos::fields::nc::datatypes, U("NcDatatypeDescriptor"), true, false, true, false, value::null())); return properties; } @@ -859,10 +895,10 @@ namespace nmos using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status property"), nc_receiver_monitor_connection_status_property_id, nmos::fields::nc::connection_status, U("NcConnectionStatus"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status message property"), nc_receiver_monitor_connection_status_message_property_id, nmos::fields::nc::connection_status_message, U("NcString"), true, true, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Payload status property"), nc_receiver_monitor_payload_status_property_id, nmos::fields::nc::payload_status, U("NcPayloadStatus"), true, false, false, false)); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Payload status message property"), nc_receiver_monitor_payload_status_message_property_id, nmos::fields::nc::payload_status_message, U("NcString"), true, true, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status property"), nc_receiver_monitor_connection_status_property_id, nmos::fields::nc::connection_status, U("NcConnectionStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status message property"), nc_receiver_monitor_connection_status_message_property_id, nmos::fields::nc::connection_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Payload status property"), nc_receiver_monitor_payload_status_property_id, nmos::fields::nc::payload_status, U("NcPayloadStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Payload status message property"), nc_receiver_monitor_payload_status_message_property_id, nmos::fields::nc::payload_status_message, U("NcString"), true, true, false, false, value::null())); return properties; } @@ -885,7 +921,7 @@ namespace nmos using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Indicates if signal protection is active"), nc_receiver_monitor_protected_signal_protection_status_property_id, nmos::fields::nc::signal_protection_status, U("NcBoolean"), true, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Indicates if signal protection is active"), nc_receiver_monitor_protected_signal_protection_status_property_id, nmos::fields::nc::signal_protection_status, U("NcBoolean"), true, false, false, false, value::null())); return properties; } @@ -908,7 +944,7 @@ namespace nmos using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Indicator active state"), nc_ident_beacon_active_property_id, nmos::fields::nc::active, U("NcBoolean"), false, false, false, false)); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Indicator active state"), nc_ident_beacon_active_property_id, nmos::fields::nc::active, U("NcBoolean"), false, false, false, false, value::null())); return properties; } @@ -930,7 +966,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(value::string(U("NcObject class descriptor")), nc_object_class_id, U("NcObject"), value::null(), make_nc_object_properties(), make_nc_object_methods(), make_nc_object_events()); + return details::make_nc_class_descriptor(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), value::null(), make_nc_object_properties(), make_nc_object_methods(), make_nc_object_events()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.1.html @@ -938,7 +974,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(value::string(U("NcBlock class descriptor")), nc_block_class_id, U("NcBlock"), value::null(), make_nc_block_properties(), make_nc_block_methods(), make_nc_block_events()); + return details::make_nc_class_descriptor(U("NcBlock class descriptor"), nc_block_class_id, U("NcBlock"), value::null(), make_nc_block_properties(), make_nc_block_methods(), make_nc_block_events()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.2.html @@ -946,7 +982,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(value::string(U("NcWorker class descriptor")), nc_worker_class_id, U("NcWorker"), value::null(), make_nc_worker_properties(), make_nc_worker_methods(), make_nc_worker_events()); + return details::make_nc_class_descriptor(U("NcWorker class descriptor"), nc_worker_class_id, U("NcWorker"), value::null(), make_nc_worker_properties(), make_nc_worker_methods(), make_nc_worker_events()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.html @@ -954,7 +990,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(value::string(U("NcManager class descriptor")), nc_manager_class_id, U("NcManager"), value::null(), make_nc_manager_properties(), make_nc_manager_methods(), make_nc_manager_events()); + return details::make_nc_class_descriptor(U("NcManager class descriptor"), nc_manager_class_id, U("NcManager"), value::null(), make_nc_manager_properties(), make_nc_manager_methods(), make_nc_manager_events()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.1.html @@ -962,7 +998,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(value::string(U("NcDeviceManager class descriptor")), nc_device_manager_class_id, U("NcDeviceManager"), value::string(U("DeviceManager")), make_nc_device_manager_properties(), make_nc_device_manager_methods(), make_nc_device_manager_events()); + return details::make_nc_class_descriptor(U("NcDeviceManager class descriptor"), nc_device_manager_class_id, U("NcDeviceManager"), value::string(U("DeviceManager")), make_nc_device_manager_properties(), make_nc_device_manager_methods(), make_nc_device_manager_events()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.2.html @@ -970,7 +1006,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(value::string(U("NcClassManager class descriptor")), nc_class_manager_class_id, U("NcClassManager"), value::string(U("ClassManager")), make_nc_class_manager_properties(), make_nc_class_manager_methods(), make_nc_class_manager_events()); + return details::make_nc_class_descriptor(U("NcClassManager class descriptor"), nc_class_manager_class_id, U("NcClassManager"), value::string(U("ClassManager")), make_nc_class_manager_properties(), make_nc_class_manager_methods(), make_nc_class_manager_events()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon @@ -978,7 +1014,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(value::string(U("NcIdentBeacon class descriptor")), nc_ident_beacon_class_id, U("NcIdentBeacon"), value::null(), make_nc_ident_beacon_properties(), make_nc_ident_beacon_methods(), make_nc_ident_beacon_events()); + return details::make_nc_class_descriptor(U("NcIdentBeacon class descriptor"), nc_ident_beacon_class_id, U("NcIdentBeacon"), value::null(), make_nc_ident_beacon_properties(), make_nc_ident_beacon_methods(), make_nc_ident_beacon_events()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor @@ -986,7 +1022,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(value::string(U("NcReceiverMonitor class descriptor")), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), value::null(), make_nc_receiver_monitor_properties(), make_nc_receiver_monitor_methods(), make_nc_receiver_monitor_events()); + return details::make_nc_class_descriptor(U("NcReceiverMonitor class descriptor"), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), value::null(), make_nc_receiver_monitor_properties(), make_nc_receiver_monitor_methods(), make_nc_receiver_monitor_events()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitorprotected @@ -994,7 +1030,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(value::string(U("NcReceiverMonitorProtected class descriptor")), nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), value::null(), make_nc_receiver_monitor_protected_properties(), make_nc_receiver_monitor_protected_methods(), make_nc_receiver_monitor_protected_events()); + return details::make_nc_class_descriptor(U("NcReceiverMonitorProtected class descriptor"), nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), value::null(), make_nc_receiver_monitor_protected_properties(), make_nc_receiver_monitor_protected_methods(), make_nc_receiver_monitor_protected_events()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcBlockMemberDescriptor.html @@ -1003,13 +1039,13 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Role of member in its containing block")), nmos::fields::nc::role, value::string(U("NcString")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("OID of member")), nmos::fields::nc::oid, value::string(U("NcOid")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff member's OID is hardwired into device")), nmos::fields::nc::constant_oid, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Class ID")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("User label")), nmos::fields::nc::user_label, value::string(U("NcString")), true, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Containing block's OID")), nmos::fields::nc::owner, value::string(U("NcOid")), false, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor which is specific to a block member")), U("NcBlockMemberDescriptor"), fields, value::string(U("NcDescriptor"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Role of member in its containing block"), nmos::fields::nc::role, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("OID of member"), nmos::fields::nc::oid, U("NcOid"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff member's OID is hardwired into device"), nmos::fields::nc::constant_oid, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Class ID"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("User label"), nmos::fields::nc::user_label, U("NcString"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Containing block's OID"), nmos::fields::nc::owner, U("NcOid"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Descriptor which is specific to a block member"), U("NcBlockMemberDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassDescriptor.html @@ -1018,13 +1054,13 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Identity of the class")), nmos::fields::nc::class_id, value::string(U("NcClassId")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of the class")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Role if the class has fixed role (manager classes)")), nmos::fields::nc::fixed_role, value::string(U("NcString")), true, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property descriptors")), nmos::fields::nc::properties, value::string(U("NcPropertyDescriptor")), false, true)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Method descriptors")), nmos::fields::nc::methods, value::string(U("NcMethodDescriptor")), false, true)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Event descriptors")), nmos::fields::nc::events, value::string(U("NcEventDescriptor")), false, true)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class")), U("NcClassDescriptor"), fields, value::string(U("NcDescriptor"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Identity of the class"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of the class"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Role if the class has fixed role (manager classes)"), nmos::fields::nc::fixed_role, U("NcString"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property descriptors"), nmos::fields::nc::properties, U("NcPropertyDescriptor"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Method descriptors"), nmos::fields::nc::methods, U("NcMethodDescriptor"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Event descriptors"), nmos::fields::nc::events, U("NcEventDescriptor"), false, true, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Descriptor of a class"), U("NcClassDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassId.html @@ -1032,7 +1068,7 @@ namespace nmos { using web::json::value; - return details::make_nc_datatype_typedef(value::string(U("Sequence of class ID fields")), U("NcClassId"), true, U("NcInt32")); + return details::make_nc_datatype_typedef(U("Sequence of class ID fields"), U("NcClassId"), true, U("NcInt32"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptor.html @@ -1041,10 +1077,10 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Datatype name")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Type: Primitive, Typedef, Struct, Enum")), nmos::fields::nc::type, value::string(U("NcDatatypeType")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Base datatype descriptor")), U("NcDatatypeDescriptor"), fields, value::string(U("NcDescriptor"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Datatype name"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Type: Primitive, Typedef, Struct, Enum"), nmos::fields::nc::type, U("NcDatatypeType"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Base datatype descriptor"), U("NcDatatypeDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorEnum.html @@ -1053,8 +1089,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("One item descriptor per enum option")), nmos::fields::nc::items, value::string(U("NcEnumItemDescriptor")), false, true)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Enum datatype descriptor")), U("NcDatatypeDescriptorEnum"), fields, value::string(U("NcDatatypeDescriptor"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("One item descriptor per enum option"), nmos::fields::nc::items, U("NcEnumItemDescriptor"), false, true, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Enum datatype descriptor"), U("NcDatatypeDescriptorEnum"), fields, U("NcDatatypeDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorPrimitive.html @@ -1063,7 +1099,7 @@ namespace nmos using web::json::value; auto fields = value::array(); - return details::make_nc_datatype_descriptor_struct(value::string(U("Primitive datatype descriptor")), U("NcDatatypeDescriptorPrimitive"), fields, value::string(U("NcDatatypeDescriptor"))); + return details::make_nc_datatype_descriptor_struct(U("Primitive datatype descriptor"), U("NcDatatypeDescriptorPrimitive"), fields, U("NcDatatypeDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorStruct.html @@ -1072,9 +1108,9 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("One item descriptor per field of the struct")), nmos::fields::nc::fields, value::string(U("NcFieldDescriptor")), false, true)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of the parent type if any or null if it has no parent")), nmos::fields::nc::parent_type, value::string(U("NcName")), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Struct datatype descriptor")), U("NcDatatypeDescriptorStruct"), fields, value::string(U("NcDatatypeDescriptor"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("One item descriptor per field of the struct"), nmos::fields::nc::fields, U("NcFieldDescriptor"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of the parent type if any or null if it has no parent"), nmos::fields::nc::parent_type, U("NcName"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Struct datatype descriptor"), U("NcDatatypeDescriptorStruct"), fields, U("NcDatatypeDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorTypeDef.html @@ -1083,9 +1119,9 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Original typedef datatype name")), nmos::fields::nc::parent_type, value::string(U("NcName")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff type is a typedef sequence of another type")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Type def datatype descriptor")), U("NcDatatypeDescriptorTypeDef"), fields, value::string(U("NcDatatypeDescriptor"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Original typedef datatype name"), nmos::fields::nc::parent_type, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff type is a typedef sequence of another type"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Type def datatype descriptor"), U("NcDatatypeDescriptorTypeDef"), fields, U("NcDatatypeDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeType.html @@ -1094,11 +1130,11 @@ namespace nmos using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Primitive datatype")), U("Primitive"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Simple alias of another datatype")), U("Typedef"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Data structure")), U("Struct"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Enum datatype")), U("Enum"), 3)); - return details::make_nc_datatype_descriptor_enum(value::string(U("Datatype type")), U("NcDatatypeType"), items); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Primitive datatype"), U("Primitive"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Simple alias of another datatype"), U("Typedef"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Data structure"), U("Struct"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Enum datatype"), U("Enum"), 3)); + return details::make_nc_datatype_descriptor_enum(U("Datatype type"), U("NcDatatypeType"), items, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDescriptor.html @@ -1107,8 +1143,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional user facing description")), nmos::fields::nc::description, value::string(U("NcString")), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Base descriptor")), U("NcDescriptor"), fields, value::null()); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional user facing description"), nmos::fields::nc::description, U("NcString"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Base descriptor"), U("NcDescriptor"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceGenericState.html @@ -1117,13 +1153,13 @@ namespace nmos using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Normal operation")), U("NormalOperation"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is initializing")), U("Initializing"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is performing a software or firmware update")), U("Updating"), 3)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is experiencing a licensing error")), U("LicensingError"), 4)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Device is experiencing an internal error")), U("InternalError"), 5)); - return details::make_nc_datatype_descriptor_enum(value::string(U("Device generic operational state")), U("NcDeviceGenericState"), items); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Unknown"), U("Unknown"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Normal operation"), U("NormalOperation"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Device is initializing"), U("Initializing"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Device is performing a software or firmware update"), U("Updating"), 3)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Device is experiencing a licensing error"), U("LicensingError"), 4)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Device is experiencing an internal error"), U("InternalError"), 5)); + return details::make_nc_datatype_descriptor_enum(U("Device generic operational state"), U("NcDeviceGenericState"), items, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceOperationalState.html @@ -1132,9 +1168,9 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Generic operational state")), nmos::fields::nc::generic_state, value::string(U("NcDeviceGenericState")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Specific device details")), nmos::fields::nc::device_specific_details, value::string(U("NcString")), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Device operational state")), U("NcDeviceOperationalState"), fields, value::null()); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Generic operational state"), nmos::fields::nc::generic_state, U("NcDeviceGenericState"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Specific device details"), nmos::fields::nc::device_specific_details, U("NcString"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Device operational state"), U("NcDeviceOperationalState"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcElementId.html @@ -1143,9 +1179,9 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Level of the element")), nmos::fields::nc::level, value::string(U("NcUint16")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Index of the element")), nmos::fields::nc::index, value::string(U("NcUint16")), false, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Class element id which contains the level and index")), U("NcElementId"), fields, value::null()); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Level of the element"), nmos::fields::nc::level, U("NcUint16"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Index of the element"), nmos::fields::nc::index, U("NcUint16"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Class element id which contains the level and index"), U("NcElementId"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEnumItemDescriptor.html @@ -1154,9 +1190,9 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of option")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Enum item numerical value")), nmos::fields::nc::value, value::string(U("NcUint16")), false, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of an enum item")), U("NcEnumItemDescriptor"), fields, value::string(U("NcDescriptor"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of option"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Enum item numerical value"), nmos::fields::nc::value, U("NcUint16"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Descriptor of an enum item"), U("NcEnumItemDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventDescriptor.html @@ -1165,11 +1201,11 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Event id with level and index")), nmos::fields::nc::id, value::string(U("NcEventId")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of event")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of event data's datatype")), nmos::fields::nc::event_datatype, value::string(U("NcName")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class event")), U("NcEventDescriptor"), fields, value::string(U("NcDescriptor"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Event id with level and index"), nmos::fields::nc::id, U("NcEventId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of event"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of event data's datatype"), nmos::fields::nc::event_datatype, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is marked as deprecated"), nmos::fields::nc::is_deprecated, U("NcBoolean"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Descriptor of a class event"), U("NcEventDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventId.html @@ -1177,7 +1213,7 @@ namespace nmos { using web::json::value; - return details::make_nc_datatype_descriptor_struct(value::string(U("Event id which contains the level and index")), U("NcEventId"), value::array(), value::string(U("NcElementId"))); + return details::make_nc_datatype_descriptor_struct(U("Event id which contains the level and index"), U("NcEventId"), value::array(), U("NcElementId"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcFieldDescriptor.html @@ -1186,12 +1222,12 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of field")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of field's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff field is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff field is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a field of a struct")), U("NcFieldDescriptor"), fields, value::string(U("NcDescriptor"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of field"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of field's datatype. Can only ever be null if the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff field is nullable"), nmos::fields::nc::is_nullable, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff field is a sequence"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Descriptor of a field of a struct"), U("NcFieldDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcId.html @@ -1199,7 +1235,7 @@ namespace nmos { using web::json::value; - return details::make_nc_datatype_typedef(value::string(U("Identity handler")), U("NcId"), false, U("NcUint32")); + return details::make_nc_datatype_typedef(U("Identity handler"), U("NcId"), false, U("NcUint32"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcManufacturer.html @@ -1208,10 +1244,10 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's name")), nmos::fields::nc::name, value::string(U("NcString")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("IEEE OUI or CID of manufacturer")), nmos::fields::nc::organization_id, value::string(U("NcOrganizationId")), true, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("URL of the manufacturer's website")), nmos::fields::nc::website, value::string(U("NcUri")), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Manufacturer descriptor")), U("NcManufacturer"), fields, value::null()); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Manufacturer's name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("IEEE OUI or CID of manufacturer"), nmos::fields::nc::organization_id, U("NcOrganizationId"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("URL of the manufacturer's website"), nmos::fields::nc::website, U("NcUri"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Manufacturer descriptor"), U("NcManufacturer"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodDescriptor.html @@ -1220,12 +1256,12 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Method id with level and index")), nmos::fields::nc::id, value::string(U("NcMethodId")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of method")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of method result's datatype")), nmos::fields::nc::result_datatype, value::string(U("NcName")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Parameter descriptors if any")), nmos::fields::nc::parameters, value::string(U("NcParameterDescriptor")), false, true)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class method")), U("NcMethodDescriptor"), fields, value::string(U("NcDescriptor"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Method id with level and index"), nmos::fields::nc::id, U("NcMethodId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of method"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of method result's datatype"), nmos::fields::nc::result_datatype, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Parameter descriptors if any"), nmos::fields::nc::parameters, U("NcParameterDescriptor"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is marked as deprecated"), nmos::fields::nc::is_deprecated, U("NcBoolean"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Descriptor of a class method"), U("NcMethodDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodId.html @@ -1233,7 +1269,7 @@ namespace nmos { using web::json::value; - return details::make_nc_datatype_descriptor_struct(value::string(U("Method id which contains the level and index")), U("NcMethodId"), value::array(), value::string(U("NcElementId"))); + return details::make_nc_datatype_descriptor_struct(U("Method id which contains the level and index"), U("NcMethodId"), value::array(), U("NcElementId"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResult.html @@ -1242,8 +1278,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Status for the invoked method")), nmos::fields::nc::status, value::string(U("NcMethodStatus")), false, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Base result of the invoked method")), U("NcMethodResult"), fields, value::null()); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Status for the invoked method"), nmos::fields::nc::status, U("NcMethodStatus"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Base result of the invoked method"), U("NcMethodResult"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultBlockMemberDescriptors.html @@ -1252,8 +1288,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Block member descriptors method result value")), nmos::fields::nc::value, value::string(U("NcBlockMemberDescriptor")), false, true)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing block member descriptors as the value")), U("NcMethodResultBlockMemberDescriptors"), fields, value::string(U("NcMethodResult"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Block member descriptors method result value"), nmos::fields::nc::value, U("NcBlockMemberDescriptor"), false, true, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Method result containing block member descriptors as the value"), U("NcMethodResultBlockMemberDescriptors"), fields, U("NcMethodResult"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultClassDescriptor.html @@ -1262,8 +1298,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Class descriptor method result value")), nmos::fields::nc::value, value::string(U("NcClassDescriptor")), false, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing a class descriptor as the value")), U("NcMethodResultClassDescriptor"), fields, value::string(U("NcMethodResult"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Class descriptor method result value"), nmos::fields::nc::value, U("NcClassDescriptor"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Method result containing a class descriptor as the value"), U("NcMethodResultClassDescriptor"), fields, U("NcMethodResult"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultDatatypeDescriptor.html @@ -1272,8 +1308,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Datatype descriptor method result value")), nmos::fields::nc::value, value::string(U("NcDatatypeDescriptor")), false, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Method result containing a datatype descriptor as the value")), U("NcMethodResultDatatypeDescriptor"), fields, value::string(U("NcMethodResult"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Datatype descriptor method result value"), nmos::fields::nc::value, U("NcDatatypeDescriptor"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Method result containing a datatype descriptor as the value"), U("NcMethodResultDatatypeDescriptor"), fields, U("NcMethodResult"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultError.html @@ -1282,8 +1318,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Error message")), nmos::fields::nc::error_message, value::string(U("NcString")), false, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Error result - to be used when the method call encounters an error")), U("NcMethodResultError"), fields, value::string(U("NcMethodResult"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Error message"), nmos::fields::nc::error_message, U("NcString"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Error result - to be used when the method call encounters an error"), U("NcMethodResultError"), fields, U("NcMethodResult"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultId.html @@ -1292,8 +1328,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Id result value")), nmos::fields::nc::value, value::string(U("NcId")), false, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Id method result")), U("NcMethodResultId"), fields, value::string(U("NcMethodResult"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Id result value"), nmos::fields::nc::value, U("NcId"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Id method result"), U("NcMethodResultId"), fields, U("NcMethodResult"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultLength.html @@ -1302,8 +1338,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Length result value")), nmos::fields::nc::value, value::string(U("NcUint32")), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Length method result")), U("NcMethodResultLength"), fields, value::string(U("NcMethodResult"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Length result value"), nmos::fields::nc::value, U("NcUint32"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Length method result"), U("NcMethodResultLength"), fields, U("NcMethodResult"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultPropertyValue.html @@ -1312,8 +1348,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Getter method value for the associated property")), nmos::fields::nc::value, value::null(), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Result when invoking the getter method associated with a property")), U("NcMethodResultPropertyValue"), fields, value::string(U("NcMethodResult"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Getter method value for the associated property"), nmos::fields::nc::value, true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Result when invoking the getter method associated with a property"), U("NcMethodResultPropertyValue"), fields, U("NcMethodResult"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodStatus.html @@ -1322,25 +1358,25 @@ namespace nmos using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful")), U("Ok"), 200)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful but targeted property is deprecated")), U("PropertyDeprecated"), 298)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call was successful but method is deprecated")), U("MethodDeprecated"), 299)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Badly-formed command (e.g. the incoming command has invalid message encoding and cannot be parsed by the underlying protocol)")), U("BadCommandFormat"), 400)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Client is not authorized")), U("Unauthorized"), 401)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Command addresses a nonexistent object")), U("BadOid"), 404)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Attempt to change read-only state")), U("Readonly"), 405)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call is invalid in current operating context (e.g. attempting to invoke a method when the object is disabled)")), U("InvalidRequest"), 406)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("There is a conflict with the current state of the device")), U("Conflict"), 409)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Something was too big")), U("BufferOverflow"), 413)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Index is outside the available range")), U("IndexOutOfBounds"), 414)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method parameter does not meet expectations (e.g. attempting to invoke a method with an invalid type for one of its parameters)")), U("ParameterError"), 417)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed object is locked")), U("Locked"), 423)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Internal device error")), U("DeviceError"), 500)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed method is not implemented by the addressed object")), U("MethodNotImplemented"), 501)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Addressed property is not implemented by the addressed object")), U("PropertyNotImplemented"), 502)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("The device is not ready to handle any commands")), U("NotReady"), 503)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Method call did not finish within the allotted time")), U("Timeout"), 504)); - return details::make_nc_datatype_descriptor_enum(value::string(U("Method invokation status")), U("NcMethodStatus"), items); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call was successful"), U("Ok"), 200)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call was successful but targeted property is deprecated"), U("PropertyDeprecated"), 298)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call was successful but method is deprecated"), U("MethodDeprecated"), 299)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Badly-formed command (e.g. the incoming command has invalid message encoding and cannot be parsed by the underlying protocol)"), U("BadCommandFormat"), 400)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Client is not authorized"), U("Unauthorized"), 401)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Command addresses a nonexistent object"), U("BadOid"), 404)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Attempt to change read-only state"), U("Readonly"), 405)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call is invalid in current operating context (e.g. attempting to invoke a method when the object is disabled)"), U("InvalidRequest"), 406)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("There is a conflict with the current state of the device"), U("Conflict"), 409)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Something was too big"), U("BufferOverflow"), 413)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Index is outside the available range"), U("IndexOutOfBounds"), 414)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method parameter does not meet expectations (e.g. attempting to invoke a method with an invalid type for one of its parameters)"), U("ParameterError"), 417)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Addressed object is locked"), U("Locked"), 423)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Internal device error"), U("DeviceError"), 500)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Addressed method is not implemented by the addressed object"), U("MethodNotImplemented"), 501)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Addressed property is not implemented by the addressed object"), U("PropertyNotImplemented"), 502)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("The device is not ready to handle any commands"), U("NotReady"), 503)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call did not finish within the allotted time"), U("Timeout"), 504)); + return details::make_nc_datatype_descriptor_enum(U("Method invokation status"), U("NcMethodStatus"), items, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcName.html @@ -1348,7 +1384,7 @@ namespace nmos { using web::json::value; - return details::make_nc_datatype_typedef(value::string(U("Programmatically significant name, alphanumerics + underscore, no spaces")), U("NcName"), false, U("NcString")); + return details::make_nc_datatype_typedef(U("Programmatically significant name, alphanumerics + underscore, no spaces"), U("NcName"), false, U("NcString"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOid.html @@ -1356,7 +1392,7 @@ namespace nmos { using web::json::value; - return details::make_nc_datatype_typedef(value::string(U("Object id")), U("NcOid"), false, U("NcUint32")); + return details::make_nc_datatype_typedef(U("Object id"), U("NcOid"), false, U("NcUint32"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOrganizationId.html @@ -1364,7 +1400,7 @@ namespace nmos { using web::json::value; - return details::make_nc_datatype_typedef(value::string(U("Unique 24-bit organization id")), U("NcOrganizationId"), false, U("NcInt32")); + return details::make_nc_datatype_typedef(U("Unique 24-bit organization id"), U("NcOrganizationId"), false, U("NcInt32"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraints.html @@ -1373,8 +1409,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Default value")), nmos::fields::nc::default_value, value::null(), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Abstract parameter constraints class")), U("NcParameterConstraints"), fields, value::null()); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Default value"), nmos::fields::nc::default_value, true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Abstract parameter constraints class"), U("NcParameterConstraints"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsNumber.html @@ -1383,10 +1419,10 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional maximum")), nmos::fields::nc::maximum, value::null(), true, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional minimum")), nmos::fields::nc::minimum, value::null(), true, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional step")), nmos::fields::nc::step, value::null(), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Number parameter constraints class")), U("NcParameterConstraintsNumber"), fields, value::string(U("NcParameterConstraints"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional maximum"), nmos::fields::nc::maximum, true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional minimum"), nmos::fields::nc::minimum, true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional step"), nmos::fields::nc::step, true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Number parameter constraints class"), U("NcParameterConstraintsNumber"), fields, U("NcParameterConstraints"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsString.html @@ -1395,9 +1431,9 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Maximum characters allowed")), nmos::fields::nc::max_characters, value::string(U("NcUint32")), true, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Regex pattern")), nmos::fields::nc::pattern, value::string(U("NcRegex")), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("String parameter constraints class")), U("NcParameterConstraintsString"), fields, value::string(U("NcParameterConstraints"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Maximum characters allowed"), nmos::fields::nc::max_characters, U("NcUint32"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Regex pattern"), nmos::fields::nc::pattern, U("NcRegex"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("String parameter constraints class"), U("NcParameterConstraintsString"), fields, U("NcParameterConstraints"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterDescriptor.html @@ -1406,12 +1442,12 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of parameter")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of parameter's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a method parameter")), U("NcParameterDescriptor"), fields, value::string(U("NcDescriptor"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of parameter"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of parameter's datatype. Can only ever be null if the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is nullable"), nmos::fields::nc::is_nullable, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is a sequence"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Descriptor of a method parameter"), U("NcParameterDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcProduct.html @@ -1420,13 +1456,13 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Product name")), nmos::fields::nc::name, value::string(U("NcString")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's unique key to product - model number, SKU, etc")), nmos::fields::nc::key, value::string(U("NcString")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Manufacturer's product revision level code")), nmos::fields::nc::revision_level, value::string(U("NcString")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Brand name under which product is sold")), nmos::fields::nc::brand_name, value::string(U("NcString")), true, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Unique UUID of product (not product instance)")), nmos::fields::nc::uuid, value::string(U("NcUuid")), true, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Text description of product")), nmos::fields::nc::description, value::string(U("NcString")), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Product descriptor")), U("NcProduct"), fields, value::null()); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Product name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Manufacturer's unique key to product - model number, SKU, etc"), nmos::fields::nc::key, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Manufacturer's product revision level code"), nmos::fields::nc::revision_level, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Brand name under which product is sold"), nmos::fields::nc::brand_name, U("NcString"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Unique UUID of product (not product instance)"), nmos::fields::nc::uuid, U("NcUuid"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Text description of product"), nmos::fields::nc::description, U("NcString"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Product descriptor"), U("NcProduct"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangeType.html @@ -1435,11 +1471,11 @@ namespace nmos using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Current value changed")), U("ValueChanged"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item added")), U("SequenceItemAdded"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item changed")), U("SequenceItemChanged"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Sequence item removed")), U("SequenceItemRemoved"), 3)); - return details::make_nc_datatype_descriptor_enum(value::string(U("Type of property change")), U("NcPropertyChangeType"), items); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Current value changed"), U("ValueChanged"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Sequence item added"), U("SequenceItemAdded"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Sequence item changed"), U("SequenceItemChanged"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Sequence item removed"), U("SequenceItemRemoved"), 3)); + return details::make_nc_datatype_descriptor_enum(U("Type of property change"), U("NcPropertyChangeType"), items, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangedEventData.html @@ -1448,11 +1484,11 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("The id of the property that changed")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Information regarding the change type")), nmos::fields::nc::change_type, value::string(U("NcPropertyChangeType")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property-type specific value")), nmos::fields::nc::value, value::null(), true, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Index of sequence item if the property is a sequence")), nmos::fields::nc::sequence_item_index, value::string(U("NcId")), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Payload of property-changed event")), U("NcPropertyChangedEventData"), fields, value::null()); + web::json::push_back(fields, details::make_nc_field_descriptor(U("The id of the property that changed"), nmos::fields::nc::property_id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Information regarding the change type"), nmos::fields::nc::change_type, U("NcPropertyChangeType"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property-type specific value"), nmos::fields::nc::value, true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Index of sequence item if the property is a sequence"), nmos::fields::nc::sequence_item_index,U("NcId"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Payload of property-changed event"), U("NcPropertyChangedEventData"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraints.html @@ -1461,9 +1497,9 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("The id of the property being constrained")), nmos::fields::nc::property_id, value::string(U("NcPropertyId")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional default value")), nmos::fields::nc::default_value, value::null(), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Property constraints class")), U("NcPropertyConstraints"), fields, value::null()); + web::json::push_back(fields, details::make_nc_field_descriptor(U("The id of the property being constrained"), nmos::fields::nc::property_id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional default value"), nmos::fields::nc::default_value, true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Property constraints class"), U("NcPropertyConstraints"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsNumber.html @@ -1472,10 +1508,10 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional maximum")), nmos::fields::nc::maximum, value::null(), true, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional minimum")), nmos::fields::nc::minimum, value::null(), true, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional step")), nmos::fields::nc::step, value::null(), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Number property constraints class")), U("NcPropertyConstraintsNumber"), fields, value::string(U("NcPropertyConstraints"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional maximum"), nmos::fields::nc::maximum, true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional minimum"), nmos::fields::nc::minimum, true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional step"), nmos::fields::nc::step, true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Number property constraints class"), U("NcPropertyConstraintsNumber"), fields, U("NcPropertyConstraints"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsString.html @@ -1484,9 +1520,9 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Maximum characters allowed")), nmos::fields::nc::max_characters, value::string(U("NcUint32")), true, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Regex pattern")), nmos::fields::nc::pattern, value::string(U("NcRegex")), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("String property constraints class")), U("NcPropertyConstraintsString"), fields, value::string(U("NcPropertyConstraints"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Maximum characters allowed"), nmos::fields::nc::max_characters, U("NcUint32"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Regex pattern"), nmos::fields::nc::pattern, U("NcRegex"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("String property constraints class"), U("NcPropertyConstraintsString"), fields, U("NcPropertyConstraints"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyDescriptor.html @@ -1495,15 +1531,15 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Property id with level and index")), nmos::fields::nc::id, value::string(U("NcPropertyId")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of property")), nmos::fields::nc::name, value::string(U("NcName")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Name of property's datatype. Can only ever be null if the type is any")), nmos::fields::nc::type_name, value::string(U("NcName")), true, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is read-only")), nmos::fields::nc::is_read_only, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is nullable")), nmos::fields::nc::is_nullable, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is a sequence")), nmos::fields::nc::is_sequence, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("TRUE iff property is marked as deprecated")), nmos::fields::nc::is_deprecated, value::string(U("NcBoolean")), false, false)); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Optional constraints on top of the underlying data type")), nmos::fields::nc::constraints, value::string(U("NcParameterConstraints")), true, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Descriptor of a class property")), U("NcPropertyDescriptor"), fields, value::string(U("NcDescriptor"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property id with level and index"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of property"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of property's datatype. Can only ever be null if the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is read-only"), nmos::fields::nc::is_read_only, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is nullable"), nmos::fields::nc::is_nullable, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is a sequence"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is marked as deprecated"), nmos::fields::nc::is_deprecated, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Descriptor of a class property"), U("NcPropertyDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyId.html @@ -1511,7 +1547,7 @@ namespace nmos { using web::json::value; - return details::make_nc_datatype_descriptor_struct(value::string(U("Property id which contains the level and index")), U("NcPropertyId"), value::array(), value::string(U("NcElementId"))); + return details::make_nc_datatype_descriptor_struct(U("Property id which contains the level and index"), U("NcPropertyId"), value::array(), U("NcElementId"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRegex.html @@ -1519,7 +1555,7 @@ namespace nmos { using web::json::value; - return details::make_nc_datatype_typedef(value::string(U("Regex pattern")), U("NcRegex"), false, U("NcString")); + return details::make_nc_datatype_typedef(U("Regex pattern"), U("NcRegex"), false, U("NcString"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcResetCause.html @@ -1528,13 +1564,13 @@ namespace nmos using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Unknown")), U("Unknown"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Power on")), U("PowerOn"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Internal error")), U("InternalError"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Upgrade")), U("Upgrade"), 3)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Controller request")), U("ControllerRequest"), 4)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Manual request from the front panel")), U("ManualReset"), 5)); - return details::make_nc_datatype_descriptor_enum(value::string(U("Reset cause enum")), U("NcResetCause"), items); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Unknown"), U("Unknown"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Power on"), U("PowerOn"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Internal error"), U("InternalError"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Upgrade"), U("Upgrade"), 3)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Controller request"), U("ControllerRequest"), 4)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Manual request from the front panel"), U("ManualReset"), 5)); + return details::make_nc_datatype_descriptor_enum(U("Reset cause enum"), U("NcResetCause"), items, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRolePath.html @@ -1542,7 +1578,7 @@ namespace nmos { using web::json::value; - return details::make_nc_datatype_typedef(value::string(U("Role path")), U("NcRolePath"), true, U("NcString")); + return details::make_nc_datatype_typedef(U("Role path"), U("NcRolePath"), true, U("NcString"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTimeInterval.html @@ -1550,7 +1586,7 @@ namespace nmos { using web::json::value; - return details::make_nc_datatype_typedef(value::string(U("Time interval described in nanoseconds")), U("NcTimeInterval"), false, U("NcInt64")); + return details::make_nc_datatype_typedef(U("Time interval described in nanoseconds"), U("NcTimeInterval"), false, U("NcInt64"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpoint.html @@ -1559,8 +1595,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Context namespace")), nmos::fields::nc::context_namespace, value::string(U("NcString")), false, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Base touchpoint class")), U("NcTouchpoint"), fields, value::null()); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Context namespace"), nmos::fields::nc::context_namespace, U("NcString"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Base touchpoint class"), U("NcTouchpoint"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmos.html @@ -1569,8 +1605,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Context NMOS resource")), nmos::fields::nc::resource, value::string(U("NcTouchpointResourceNmos")), false, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Touchpoint class for NMOS resources")), U("NcTouchpointNmos"), fields, value::string(U("NcTouchpoint"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Context NMOS resource"), nmos::fields::nc::resource, U("NcTouchpointResourceNmos"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Touchpoint class for NMOS resources"), U("NcTouchpointNmos"), fields, U("NcTouchpoint"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmosChannelMapping.html @@ -1579,8 +1615,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("Context Channel Mapping resource")), nmos::fields::nc::resource, value::string(U("NcTouchpointResourceNmosChannelMapping")), false, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Touchpoint class for NMOS IS-08 resources")), U("NcTouchpointNmosChannelMapping"), fields, value::string(U("NcTouchpoint"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Context Channel Mapping resource"), nmos::fields::nc::resource,U("NcTouchpointResourceNmosChannelMapping"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Touchpoint class for NMOS IS-08 resources"), U("NcTouchpointNmosChannelMapping"), fields, U("NcTouchpoint"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResource.html @@ -1589,8 +1625,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("The type of the resource")), nmos::fields::nc::resource_type, value::string(U("NcString")), false, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Touchpoint resource class")), U("NcTouchpointResource"), fields, value::null()); + web::json::push_back(fields, details::make_nc_field_descriptor(U("The type of the resource"), nmos::fields::nc::resource_type, U("NcString"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Touchpoint resource class"), U("NcTouchpointResource"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmos.html @@ -1599,8 +1635,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("NMOS resource UUID")), nmos::fields::nc::id, value::string(U("NcUuid")), false, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Touchpoint resource class for NMOS resources")), U("NcTouchpointResourceNmos"), fields, value::string(U("NcTouchpointResource"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("NMOS resource UUID"), nmos::fields::nc::id, U("NcUuid"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Touchpoint resource class for NMOS resources"), U("NcTouchpointResourceNmos"), fields, U("NcTouchpointResource"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmosChannelMapping.html @@ -1609,8 +1645,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(value::string(U("IS-08 Audio Channel Mapping input or output id")), nmos::fields::nc::io_id, value::string(U("NcString")), false, false)); - return details::make_nc_datatype_descriptor_struct(value::string(U("Touchpoint resource class for NMOS resources")), U("NcTouchpointResourceNmosChannelMapping"), fields, value::string(U("NcTouchpointResourceNmos"))); + web::json::push_back(fields, details::make_nc_field_descriptor(U("IS-08 Audio Channel Mapping input or output id"), nmos::fields::nc::io_id, U("NcString"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Touchpoint resource class for NMOS resources"), U("NcTouchpointResourceNmosChannelMapping"), fields, U("NcTouchpointResourceNmos"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUri.html @@ -1618,7 +1654,7 @@ namespace nmos { using web::json::value; - return details::make_nc_datatype_typedef(value::string(U("Uniform resource identifier")), U("NcUri"), false, U("NcString")); + return details::make_nc_datatype_typedef(U("Uniform resource identifier"), U("NcUri"), false, U("NcString"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUuid.html @@ -1626,7 +1662,7 @@ namespace nmos { using web::json::value; - return details::make_nc_datatype_typedef(value::string(U("UUID")), U("NcUuid"), false, U("NcString")); + return details::make_nc_datatype_typedef(U("UUID"), U("NcUuid"), false, U("NcString"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcVersionCode.html @@ -1634,7 +1670,7 @@ namespace nmos { using web::json::value; - return details::make_nc_datatype_typedef(value::string(U("Version code in semantic versioning format")), U("NcVersionCode"), false, U("NcString")); + return details::make_nc_datatype_typedef(U("Version code in semantic versioning format"), U("NcVersionCode"), false, U("NcString"), value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncconnectionstatus @@ -1643,11 +1679,11 @@ namespace nmos using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("This is the value when there is no receiver")), U("Undefined"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Connected to a stream")), U("Connected"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Not connected to a stream")), U("Disconnected"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("A connection error was encountered")), U("ConnectionError"), 3)); - return details::make_nc_datatype_descriptor_enum(value::string(U("Connection status enum data typee")), U("NcConnectionStatus"), items); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("This is the value when there is no receiver"), U("Undefined"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Connected to a stream"), U("Connected"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Not connected to a stream"), U("Disconnected"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("A connection error was encountered"), U("ConnectionError"), 3)); + return details::make_nc_datatype_descriptor_enum(U("Connection status enum data typee"), U("NcConnectionStatus"), items, value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncpayloadstatus @@ -1656,10 +1692,10 @@ namespace nmos using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("This is the value when there's no connection")), U("Undefined"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Payload is being received without errors and is the correct type")), U("PayloadOK"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("Payload is being received but is of an unsupported type")), U("PayloadFormatUnsupported"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(value::string(U("A payload error was encountered")), U("PayloadError"), 3)); - return details::make_nc_datatype_descriptor_enum(value::string(U("Connection status enum data typee")), U("NcPayloadStatus"), items); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("This is the value when there's no connection"), U("Undefined"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Payload is being received without errors and is the correct type"), U("PayloadOK"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Payload is being received but is of an unsupported type"), U("PayloadFormatUnsupported"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("A payload error was encountered"), U("PayloadError"), 3)); + return details::make_nc_datatype_descriptor_enum(U("Connection status enum data typee"), U("NcPayloadStatus"), items, value::null()); } } diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index bff521676..99f82c497 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -88,8 +88,9 @@ namespace nmos // description can be null // type_name can be null // constraints can be null - web::json::value make_nc_field_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); - web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_field_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); + web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); + web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethoddescriptor // description can be null @@ -101,9 +102,9 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterdescriptor // description can be null // type_name can be null - web::json::value make_nc_parameter_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); - web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); - web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_parameter_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); + web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints); + web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertydescriptor // description can be null @@ -111,37 +112,42 @@ namespace nmos // type_name can be null // constraints can be null web::json::value make_nc_property_descriptor(const web::json::value& description, const nc_property_id& id, const nc_name& name, const web::json::value& type_name, - bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints = web::json::value::null()); + bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints); web::json::value make_nc_property_descriptor(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, - bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints = web::json::value::null()); + bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptor // description can be null // constraints can be null - web::json::value make_nc_datatype_descriptor(const web::json::value& description, const nc_name& name, nc_datatype_type::type type, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_datatype_descriptor(const web::json::value& description, const nc_name& name, nc_datatype_type::type type, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorenum // description can be null // constraints can be null // items: sequence - web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints); + web::json::value make_nc_datatype_descriptor_enum(const utility::string_t& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorprimitive // description can be null // constraints can be null - web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const nc_name& name, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const nc_name& name, const web::json::value& constraints); + web::json::value make_nc_datatype_descriptor_primitive(const utility::string_t& description, const nc_name& name, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorstruct // description can be null // constraints can be null // fields: sequence // parent_type can be null - web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const nc_name& name, const web::json::value& fields, const web::json::value& parent_type, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const nc_name& name, const web::json::value& fields, const web::json::value& parent_type, const web::json::value& constraints); + web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const utility::string_t& parent_type, const web::json::value& constraints); + web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptortypedef // description can be null // constraints can be null - web::json::value make_nc_datatype_typedef(const web::json::value& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints = web::json::value::null()); + web::json::value make_nc_datatype_typedef(const web::json::value& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints); + web::json::value make_nc_datatype_typedef(const utility::string_t& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); From de5beed75397b2adad42a59533a736e76bb474fe Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 19 Sep 2023 13:28:41 +0100 Subject: [PATCH 047/250] Add description to nc_object to simplify create nc_xxx class --- .../nmos-cpp-node/node_implementation.cpp | 38 ++++----- .../nmos/control_protocol_resource.cpp | 27 +++---- Development/nmos/control_protocol_resource.h | 12 +-- .../nmos/control_protocol_resources.cpp | 77 ++----------------- Development/nmos/control_protocol_resources.h | 9 +-- 5 files changed, 45 insertions(+), 118 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 8d3588c6e..ee8565b55 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -920,9 +920,9 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr control_protocol_state.insert(gain_control_class); } // helper function to create Gain control - auto make_gain_control = [&gain_value, &gain_control_class_id](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, float gain = 0.0, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null()) + auto make_gain_control = [&gain_value, &gain_control_class_id](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, float gain = 0.0, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null()) { - auto data = nmos::details::make_nc_worker(gain_control_class_id, oid, true, owner, role, value::string(user_label), touchpoints, runtime_property_constraints, true); + auto data = nmos::details::make_nc_worker(gain_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); data[gain_value] = value::number(gain); return nmos::resource{ nmos::is12_versions::v1_0, nmos::types::nc_object, std::move(data), true }; @@ -1064,7 +1064,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr }); }; // helper function to create Example control - auto make_example_control = [&](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, + auto make_example_control = [&](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, example_enum enum_property_ = example_enum::Undefined, const utility::string_t& string_property_ = U(""), uint64_t number_property_ = 0, @@ -1080,7 +1080,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr std::vector object_sequence_ = {}, const value& touchpoints = value::null(), const value& runtime_property_constraints = value::null()) { - auto data = nmos::details::make_nc_worker(example_control_class_id, oid, true, owner, role, value::string(user_label), touchpoints, runtime_property_constraints, true); + auto data = nmos::details::make_nc_worker(example_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); data[enum_property] = value::number(enum_property_); data[string_property] = value::string(string_property_); data[number_property] = value::number(number_property_); @@ -1132,26 +1132,26 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // example stereo gain const auto stereo_gain_oid = ++oid; - auto stereo_gain = nmos::make_block(stereo_gain_oid, nmos::root_block_oid, U("stereo-gain"), U("Stereo gain")); + auto stereo_gain = nmos::make_block(stereo_gain_oid, nmos::root_block_oid, U("stereo-gain"), U("Stereo gain"), U("Stereo gain block")); // example channel gain const auto channel_gain_oid = ++oid; - auto channel_gain = nmos::make_block(channel_gain_oid, stereo_gain_oid, U("channel-gain"), U("Channel gain")); + auto channel_gain = nmos::make_block(channel_gain_oid, stereo_gain_oid, U("channel-gain"), U("Channel gain"), U("Channel gain block")); // example left/right gains - auto left_gain = make_gain_control(++oid, channel_gain_oid, U("left-gain"), U("Left gain")); - auto right_gain = make_gain_control(++oid, channel_gain_oid, U("right-gain"), U("Right gain")); + auto left_gain = make_gain_control(++oid, channel_gain_oid, U("left-gain"), U("Left gain"), U("Left channel gain")); + auto right_gain = make_gain_control(++oid, channel_gain_oid, U("right-gain"), U("Right gain"), U("Right channel gain")); // add left-gain and right-gain to channel gain - nmos::add_member(U("Left channel gain"), left_gain, channel_gain); - nmos::add_member(U("Right channel gain"), right_gain, channel_gain); + nmos::push_back(channel_gain, left_gain); + nmos::push_back(channel_gain, right_gain); // example master-gain - auto master_gain = make_gain_control(++oid, channel_gain_oid, U("master-gain"), U("Master gain")); - // add master-gain and channel-gain to stereo-gain - nmos::add_member(U("Master gain block"), master_gain, stereo_gain); - nmos::add_member(U("Channel gain block"), channel_gain, stereo_gain); + auto master_gain = make_gain_control(++oid, channel_gain_oid, U("master-gain"), U("Master gain"), U("Master gain block")); + // add channel-gain and master-gain to stereo-gain + nmos::push_back(stereo_gain, channel_gain); + nmos::push_back(stereo_gain, master_gain); // example example-control - auto example_control = make_example_control(++oid, nmos::root_block_oid, U("ExampleControl"), U("Example control worker"), + auto example_control = make_example_control(++oid, nmos::root_block_oid, U("ExampleControl"), U("Example control worker"), U("Example control worker"), example_enum::Undefined, U("test"), 3, @@ -1168,13 +1168,13 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr ); // add example-control to root-block - nmos::add_member(U("Example control worker"), example_control, root_block); + nmos::push_back(root_block, example_control); // add stereo-gain to root-block - nmos::add_member(U("Stereo gain block"), stereo_gain, root_block); + nmos::push_back(root_block, stereo_gain); // add class-manager to root-block - nmos::add_member(U("The class manager offers access to control class and data type descriptors"), class_manager, root_block); + nmos::push_back(root_block, class_manager); // add device-manager to root-block - nmos::add_member(U("The device manager offers information about the product this device is representing"), device_manager, root_block); + nmos::push_back(root_block, device_manager); // insert resources to model if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(example_control), gate)) throw node_implementation_init_exception(); diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 8d89358b2..afc578156 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -1,6 +1,5 @@ #include "nmos/control_protocol_resource.h" -//#include "nmos/resource.h" #include "nmos/control_protocol_state.h" // for nmos::experimental::control_classes definitions #include "nmos/json_fields.h" @@ -445,14 +444,12 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject - web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { using web::json::value; const auto id = utility::conversions::details::to_string_t(oid); -// auto data = make_resource_core(id, user_label.is_null() ? U("") : user_label.as_string(), {}); - value data; - data[nmos::fields::id] = value::string(id); // required for nmos::resource + auto data = make_resource_core(id, user_label.is_null() ? U("") : user_label.as_string(), description); // required for nmos::resource data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); data[nmos::fields::nc::oid] = oid; data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); @@ -466,11 +463,11 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock - web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members) + web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members) { using web::json::value; - auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); + auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); data[nmos::fields::nc::enabled] = value::boolean(enabled); data[nmos::fields::nc::members] = members; @@ -478,30 +475,30 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker - web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled) + web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled) { using web::json::value; - auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); + auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); data[nmos::fields::nc::enabled] = value::boolean(enabled); return data; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager - web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { - return make_nc_object(class_id, oid, constant_oid, owner, role, user_label, touchpoints, runtime_property_constraints); + return make_nc_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager - web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, + web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause) { using web::json::value; - auto data = details::make_nc_manager(nc_device_manager_class_id, oid, true, owner, U("DeviceManager"), user_label, touchpoints, runtime_property_constraints); + auto data = details::make_nc_manager(nc_device_manager_class_id, oid, true, owner, U("DeviceManager"), user_label, description, touchpoints, runtime_property_constraints); data[nmos::fields::nc::nc_version] = value::string(U("v1.0.0")); data[nmos::fields::nc::manufacturer] = manufacturer; data[nmos::fields::nc::product] = product; @@ -517,11 +514,11 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state) + web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state) { using web::json::value; - auto data = make_nc_manager(nc_class_manager_class_id, oid, true, owner, U("ClassManager"), user_label, touchpoints, runtime_property_constraints); + auto data = make_nc_manager(nc_class_manager_class_id, oid, true, owner, U("ClassManager"), user_label, description, touchpoints, runtime_property_constraints); // add control classes data[nmos::fields::nc::control_classes] = value::array(); diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 99f82c497..2479a7f9f 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -150,24 +150,24 @@ namespace nmos web::json::value make_nc_datatype_typedef(const utility::string_t& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject - web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); + web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock - web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members); + web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker - web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled); + web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager - web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); + web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager - web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, + web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state); + web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state); } // message response diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index b7e28fd29..f576fd44d 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -2,7 +2,6 @@ #include "nmos/control_protocol_resource.h" #include "nmos/control_protocol_utils.h" -#include "nmos/query_utils.h" #include "nmos/resource.h" #include "nmos/is12_versions.h" @@ -11,22 +10,22 @@ namespace nmos namespace details { // create block resource - resource make_block(nmos::nc_oid oid, const web::json::value& owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) + resource make_block(nmos::nc_oid oid, const web::json::value& owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) { using web::json::value; - auto data = details::make_nc_block(nc_block_class_id, oid, true, owner, role, value::string(user_label), touchpoints, runtime_property_constraints, true, members); + auto data = details::make_nc_block(nc_block_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true, members); return{ is12_versions::v1_0, types::nc_object, std::move(data), true }; } } // create block resource - resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) + resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) { using web::json::value; - return details::make_block(oid, value(owner), role, user_label, touchpoints, runtime_property_constraints, members); + return details::make_block(oid, value(owner), role, user_label, description, touchpoints, runtime_property_constraints, members); } // create Root block resource @@ -34,7 +33,7 @@ namespace nmos { using web::json::value; - return details::make_block(1, value::null(), U("root"), U("Root"), value::null(), value::null(), value::array()); + return details::make_block(1, value::null(), U("root"), U("Root"), U("Root block"), value::null(), value::null(), value::array()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager @@ -42,7 +41,6 @@ namespace nmos { using web::json::value; - const auto user_label = value::string(U("Device manager")); const auto& manufacturer = details::make_nc_manufacturer(nmos::experimental::fields::manufacturer_name(settings)); const auto& product = details::make_nc_product(nmos::experimental::fields::product_name(settings), nmos::experimental::fields::product_key(settings), nmos::experimental::fields::product_key(settings)); const auto& serial_number = nmos::experimental::fields::serial_number(settings); @@ -50,7 +48,7 @@ namespace nmos const auto device_role = value::null(); const auto& operational_state = details::make_nc_device_operational_state(nc_device_generic_state::normal_operation, value::null()); - auto data = details::make_nc_device_manager(oid, root_block_oid, user_label, value::null(), value::null(), + auto data = details::make_nc_device_manager(oid, root_block_oid, value::string(U("Device manager")), U("The device manager offers information about the product this device is representing"), value::null(), value::null(), manufacturer, product, serial_number, value::null(), device_name, device_role, operational_state, nc_reset_cause::unknown); return{ is12_versions::v1_0, types::nc_object, std::move(data), true }; @@ -61,69 +59,8 @@ namespace nmos { using web::json::value; - const auto user_label = value::string(U("Class manager")); - - auto data = details::make_nc_class_manager(oid, root_block_oid, user_label, value::null(), value::null(), control_protocol_state); + auto data = details::make_nc_class_manager(oid, root_block_oid, value::string(U("Class manager")), U("The class manager offers access to control class and data type descriptors"), value::null(), value::null(), control_protocol_state); return{ is12_versions::v1_0, types::nc_object, std::move(data), true }; } - - // add to owner block member - bool add_member(const utility::string_t& child_description, const nmos::resource& child_block, nmos::resource& parent_block) - { - using web::json::value; - - auto& parent = parent_block.data; - const auto& child = child_block.data; - - web::json::push_back(parent[nmos::fields::nc::members], - details::make_nc_block_member_descriptor(child_description, nmos::fields::nc::role(child), nmos::fields::nc::oid(child), nmos::fields::nc::constant_oid(child), details::parse_nc_class_id(nmos::fields::nc::class_id(child)), nmos::fields::nc::user_label(child), nmos::fields::nc::oid(parent))); - - return true; - } - - // modify a resource, and insert notification event to all subscriptions - bool modify_control_protocol_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event) - { - auto found = resources.find(id); - if (resources.end() == found || !found->has_data()) return false; - - auto pre = found->data; - - // "If an exception is thrown by some user-provided operation, then the element pointed to by position is erased." - // This seems too surprising, despite the fact that it means that a modification may have been partially completed, - // so capture and rethrow. - // See https://www.boost.org/doc/libs/1_68_0/libs/multi_index/doc/reference/ord_indices.html#modify - std::exception_ptr modifier_exception; - - auto resource_updated = nmos::strictly_increasing_update(resources); - auto result = resources.modify(found, [&resource_updated, &modifier, &modifier_exception](resource& resource) - { - try - { - modifier(resource); - } - catch (...) - { - modifier_exception = std::current_exception(); - } - - // set the update timestamp - resource.updated = resource_updated; - }); - - if (result) - { - auto& modified = *found; - - insert_notification_events(resources, modified.version, modified.downgrade_version, modified.type, pre, modified.data, notification_event); - } - - if (modifier_exception) - { - std::rethrow_exception(modifier_exception); - } - - return result; - } } diff --git a/Development/nmos/control_protocol_resources.h b/Development/nmos/control_protocol_resources.h index 56f80103f..c1718c0a0 100644 --- a/Development/nmos/control_protocol_resources.h +++ b/Development/nmos/control_protocol_resources.h @@ -2,7 +2,6 @@ #define NMOS_CONTROL_PROTOCOL_RESOURCES_H #include "nmos/control_protocol_typedefs.h" // for details::nc_oid definition -#include "nmos/resources.h" #include "nmos/settings.h" namespace nmos @@ -15,7 +14,7 @@ namespace nmos struct resource; // create block resource - resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), const web::json::value& members = web::json::value::array()); + resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), const web::json::value& members = web::json::value::array()); // create Root block resource resource make_root_block(); @@ -25,12 +24,6 @@ namespace nmos // create Class manager resource resource make_class_manager(nc_oid oid, const nmos::experimental::control_protocol_state& control_protocol_state); - - // add to owner block member - bool add_member(const utility::string_t& child_description, const resource& child_block, resource& parent_block); - - // modify a resource, and insert notification event to all subscriptions - bool modify_control_protocol_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event); } #endif From fa2a4ba0fd8aea25124e939afb9ae437d9948930 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 19 Sep 2023 13:35:12 +0100 Subject: [PATCH 048/250] Move nc helper functions to nc utils --- Development/nmos/control_protocol_methods.cpp | 8 +-- Development/nmos/control_protocol_utils.cpp | 58 +++++++++++++++++++ Development/nmos/control_protocol_utils.h | 6 ++ 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index 200f8768a..6d8a93dea 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -63,7 +63,7 @@ namespace nmos const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, { parse_nc_property_id(property_id), nc_property_change_type::type::value_changed, val }); const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); - modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) + modify_resource(resources, resource->id, [&](nmos::resource& resource) { resource.data[nmos::fields::nc::name(property)] = val; @@ -149,7 +149,7 @@ namespace nmos const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, { parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_changed, val, nc_id(index) }); const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); - modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) + modify_resource(resources, resource->id, [&](nmos::resource& resource) { resource.data[nmos::fields::nc::name(property)][index] = val; @@ -200,7 +200,7 @@ namespace nmos const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, { parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_added, val, sequence_item_index }); const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); - modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) + modify_resource(resources, resource->id, [&](nmos::resource& resource) { auto& sequence = resource.data[nmos::fields::nc::name(property)]; if (data.is_null()) { sequence = value::array(); } @@ -246,7 +246,7 @@ namespace nmos const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, { parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_removed, data.as_array().at(index), nc_id(index)}); const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); - modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) + modify_resource(resources, resource->id, [&](nmos::resource& resource) { auto& sequence = resource.data[nmos::fields::nc::name(property)].as_array(); sequence.erase(index); diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index de9e00713..641d95f49 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -7,6 +7,7 @@ #include "nmos/control_protocol_resource.h" #include "nmos/control_protocol_state.h" #include "nmos/json_fields.h" +#include "nmos/query_utils.h" #include "nmos/resources.h" namespace nmos @@ -210,4 +211,61 @@ namespace nmos } } } + + // add block (NcBlock) to other block (NcBlock) + void push_back(nmos::resource& parent_block, const nmos::resource& child_block) + { + using web::json::value; + + auto& parent = parent_block.data; + const auto& child = child_block.data; + + web::json::push_back(parent[nmos::fields::nc::members], + details::make_nc_block_member_descriptor(nmos::fields::description(child), nmos::fields::nc::role(child), nmos::fields::nc::oid(child), nmos::fields::nc::constant_oid(child), details::parse_nc_class_id(nmos::fields::nc::class_id(child)), nmos::fields::nc::user_label(child), nmos::fields::nc::oid(parent))); + } + + // modify a resource, and insert notification event to all subscriptions + bool modify_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event) + { + auto found = resources.find(id); + if (resources.end() == found || !found->has_data()) return false; + + auto pre = found->data; + + // "If an exception is thrown by some user-provided operation, then the element pointed to by position is erased." + // This seems too surprising, despite the fact that it means that a modification may have been partially completed, + // so capture and rethrow. + // See https://www.boost.org/doc/libs/1_68_0/libs/multi_index/doc/reference/ord_indices.html#modify + std::exception_ptr modifier_exception; + + auto resource_updated = nmos::strictly_increasing_update(resources); + auto result = resources.modify(found, [&resource_updated, &modifier, &modifier_exception](resource& resource) + { + try + { + modifier(resource); + } + catch (...) + { + modifier_exception = std::current_exception(); + } + + // set the update timestamp + resource.updated = resource_updated; + }); + + if (result) + { + auto& modified = *found; + + insert_notification_events(resources, modified.version, modified.downgrade_version, modified.type, pre, modified.data, notification_event); + } + + if (modifier_exception) + { + std::rethrow_exception(modifier_exception); + } + + return result; + } } diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index 47838ddd6..2a47ae831 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -32,6 +32,12 @@ namespace nmos // find members with given class id void find_members_by_class_id(const resources& resources, resources::iterator resource, const nc_class_id& class_id, bool include_derived, bool recurse, web::json::array& descriptors); + + // add block (NcBlock) to other block (NcBlock) + void push_back(resource& parent_block, const resource& child_block); + + // modify a resource, and insert notification event to all subscriptions + bool modify_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event); } #endif From ac8f21ba73fbab4cdbd8cdc462c757c664219f52 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 19 Sep 2023 22:32:35 +0100 Subject: [PATCH 049/250] look before accessing control_protocol_state --- Development/nmos/control_protocol_resource.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index afc578156..acf369cac 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -520,6 +520,8 @@ namespace nmos auto data = make_nc_manager(nc_class_manager_class_id, oid, true, owner, U("ClassManager"), user_label, description, touchpoints, runtime_property_constraints); + auto lock = control_protocol_state.read_lock(); + // add control classes data[nmos::fields::nc::control_classes] = value::array(); auto& control_classes = data[nmos::fields::nc::control_classes]; From a805d8a988f30645b127b2ce1e3c51fc96b1c6db Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 19 Sep 2023 22:34:53 +0100 Subject: [PATCH 050/250] pusback allows on NcBlock only --- Development/nmos/control_protocol_utils.cpp | 11 ++++++++--- Development/nmos/control_protocol_utils.h | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 641d95f49..5fa97bd8c 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -213,15 +213,20 @@ namespace nmos } // add block (NcBlock) to other block (NcBlock) - void push_back(nmos::resource& parent_block, const nmos::resource& child_block) + bool push_back(resource& parent_block, const resource& child_block) { using web::json::value; auto& parent = parent_block.data; const auto& child = child_block.data; - web::json::push_back(parent[nmos::fields::nc::members], - details::make_nc_block_member_descriptor(nmos::fields::description(child), nmos::fields::nc::role(child), nmos::fields::nc::oid(child), nmos::fields::nc::constant_oid(child), details::parse_nc_class_id(nmos::fields::nc::class_id(child)), nmos::fields::nc::user_label(child), nmos::fields::nc::oid(parent))); + if (is_nc_block(details::parse_nc_class_id(nmos::fields::nc::class_id(parent))) ) + { + web::json::push_back(parent[nmos::fields::nc::members], + details::make_nc_block_member_descriptor(nmos::fields::description(child), nmos::fields::nc::role(child), nmos::fields::nc::oid(child), nmos::fields::nc::constant_oid(child), details::parse_nc_class_id(nmos::fields::nc::class_id(child)), nmos::fields::nc::user_label(child), nmos::fields::nc::oid(parent))); + return true; + } + return false; } // modify a resource, and insert notification event to all subscriptions diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index 2a47ae831..2ab06e9e7 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -34,7 +34,7 @@ namespace nmos void find_members_by_class_id(const resources& resources, resources::iterator resource, const nc_class_id& class_id, bool include_derived, bool recurse, web::json::array& descriptors); // add block (NcBlock) to other block (NcBlock) - void push_back(resource& parent_block, const resource& child_block); + bool push_back(resource& parent_block, const resource& child_block); // modify a resource, and insert notification event to all subscriptions bool modify_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event); From 4873c40bd6b882a6757e486d98e0bcbc08ef304c Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 19 Sep 2023 22:35:42 +0100 Subject: [PATCH 051/250] Add control protocol unit tests --- Development/cmake/NmosCppTest.cmake | 1 + .../nmos/test/control_protocol_test.cpp | 628 ++++++++++++++++++ 2 files changed, 629 insertions(+) create mode 100644 Development/nmos/test/control_protocol_test.cpp diff --git a/Development/cmake/NmosCppTest.cmake b/Development/cmake/NmosCppTest.cmake index 02db14903..c0819bcf4 100644 --- a/Development/cmake/NmosCppTest.cmake +++ b/Development/cmake/NmosCppTest.cmake @@ -42,6 +42,7 @@ set(NMOS_CPP_TEST_NMOS_TEST_SOURCES nmos/test/api_utils_test.cpp nmos/test/capabilities_test.cpp nmos/test/channels_test.cpp + nmos/test/control_protocol_test.cpp nmos/test/did_sdid_test.cpp nmos/test/event_type_test.cpp nmos/test/json_validator_test.cpp diff --git a/Development/nmos/test/control_protocol_test.cpp b/Development/nmos/test/control_protocol_test.cpp new file mode 100644 index 000000000..8b44e335b --- /dev/null +++ b/Development/nmos/test/control_protocol_test.cpp @@ -0,0 +1,628 @@ +// The first "test" is of course whether the header compiles standalone +#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_typedefs.h" +#include "nmos/json_fields.h" + +#include "bst/test/test.h" + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testNcObject) +{ + using web::json::value_of; + using web::json::value; + + // NcObject + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.html + + const auto property_class_id = value_of({ + { U("description"), U("Static value. All instances of the same class will have the same identity value") }, + { U("id"), value_of({ + { U("level"), 1 }, + { U("index"), 1 } + }) }, + { U("name"), U("classId") }, + { U("typeName"), U("NcClassId") }, + { U("isReadOnly"), true }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("isDeprecated"), false }, + { U("constraints"), value::null() } + }); + const auto property_class_id_ = nmos::details::make_nc_property_descriptor(U("Static value. All instances of the same class will have the same identity value"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, value::null()); + BST_REQUIRE_EQUAL(property_class_id, property_class_id_); + + const auto property_oid = value_of({ + { U("description"), U("Object identifier") }, + { U("id"), value_of({ + { U("level"), 1 }, + { U("index"), 2 } + }) }, + { U("name"), U("oid") }, + { U("typeName"), U("NcOid") }, + { U("isReadOnly"), true }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("isDeprecated"), false }, + { U("constraints"), value::null() } + }); + const auto property_oid_ = nmos::details::make_nc_property_descriptor(U("Object identifier"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, value::null()); + BST_REQUIRE_EQUAL(property_oid, property_oid_); + + const auto property_constant_oid = value_of({ + { U("description"), U("TRUE iff OID is hardwired into device") }, + { U("id"), value_of({ + { U("level"), 1 }, + { U("index"), 3 } + }) }, + { U("name"), U("constantOid") }, + { U("typeName"), U("NcBoolean") }, + { U("isReadOnly"), true }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("isDeprecated"), false }, + { U("constraints"), value::null() } + }); + const auto property_constant_oid_ = nmos::details::make_nc_property_descriptor(U("TRUE iff OID is hardwired into device"), nmos::nc_object_constant_oid_property_id, nmos::fields::nc::constant_oid, U("NcBoolean"), true, false, false, false, value::null()); + BST_REQUIRE_EQUAL(property_constant_oid, property_constant_oid_); + + const auto property_owner = value_of({ + { U("description"), U("OID of containing block. Can only ever be null for the root block") }, + { U("id"), value_of({ + { U("level"), 1 }, + { U("index"), 4 } + }) }, + { U("name"), U("owner") }, + { U("typeName"), U("NcOid") }, + { U("isReadOnly"), true }, + { U("isNullable"), true }, + { U("isSequence"), false }, + { U("isDeprecated"), false }, + { U("constraints"), value::null() } + }); + const auto property_owner_ = nmos::details::make_nc_property_descriptor(U("OID of containing block. Can only ever be null for the root block"), nmos::nc_object_owner_property_id, nmos::fields::nc::owner, U("NcOid"), true, true, false, false, value::null()); + BST_REQUIRE_EQUAL(property_owner, property_owner_); + + const auto property_role = value_of({ + { U("description"), U("Role of object in the containing block") }, + { U("id"), value_of({ + { U("level"), 1 }, + { U("index"), 5 } + }) }, + { U("name"), U("role") }, + { U("typeName"), U("NcString") }, + { U("isReadOnly"), true }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("isDeprecated"), false }, + { U("constraints"), value::null() } + }); + const auto property_role_ = nmos::details::make_nc_property_descriptor(U("Role of object in the containing block"), nmos::nc_object_role_property_id, nmos::fields::nc::role, U("NcString"), true, false, false, false, value::null()); + BST_REQUIRE_EQUAL(property_role, property_role_); + + const auto property_user_label = value_of({ + { U("description"), U("Scribble strip") }, + { U("id"), value_of({ + { U("level"), 1 }, + { U("index"), 6 } + }) }, + { U("name"), U("userLabel") }, + { U("typeName"), U("NcString") }, + { U("isReadOnly"), false }, + { U("isNullable"), true }, + { U("isSequence"), false }, + { U("isDeprecated"), false }, + { U("constraints"), value::null() } + }); + const auto property_user_label_ = nmos::details::make_nc_property_descriptor(U("Scribble strip"), nmos::nc_object_user_label_property_id, nmos::fields::nc::user_label, U("NcString"), false, true, false, false, value::null()); + BST_REQUIRE_EQUAL(property_user_label, property_user_label_); + + const auto property_touchpoints = value_of({ + { U("description"), U("Touchpoints to other contexts") }, + { U("id"), value_of({ + { U("level"), 1 }, + { U("index"), 7 } + }) }, + { U("name"), U("touchpoints") }, + { U("typeName"), U("NcTouchpoint") }, + { U("isReadOnly"), true }, + { U("isNullable"), true }, + { U("isSequence"), true }, + { U("isDeprecated"), false }, + { U("constraints"), value::null() } + }); + const auto property_touchpoints_ = nmos::details::make_nc_property_descriptor(U("Touchpoints to other contexts"), nmos::nc_object_touchpoints_property_id, nmos::fields::nc::touchpoints, U("NcTouchpoint"), true, true, true, false, value::null()); + BST_REQUIRE_EQUAL(property_touchpoints, property_touchpoints_); + + const auto property_runtime_property_constraints = value_of({ + { U("description"), U("Runtime property constraints") }, + { U("id"), value_of({ + { U("level"), 1 }, + { U("index"), 8 } + }) }, + { U("name"), U("runtimePropertyConstraints") }, + { U("typeName"), U("NcPropertyConstraints") }, + { U("isReadOnly"), true }, + { U("isNullable"), true }, + { U("isSequence"), true }, + { U("isDeprecated"), false }, + { U("constraints"), value::null() } + }); + const auto property_runtime_property_constraints_ = nmos::details::make_nc_property_descriptor(U("Runtime property constraints"), nmos::nc_object_runtime_property_constraints_property_id, nmos::fields::nc::runtime_property_constraints, U("NcPropertyConstraints"), true, true, true, false, value::null()); + BST_REQUIRE_EQUAL(property_runtime_property_constraints, property_runtime_property_constraints_); + + const auto method_get = value_of({ + { U("description"), U("Get property value") }, + { U("id"), value_of({ + { U("level"), 1 }, + { U("index"), 1 } + }) }, + { U("name"), U("Get") }, + { U("resultDatatype"), U("NcMethodResultPropertyValue") }, + { U("parameters"), value_of({ + value_of({ + { U("description"), U("Property id") }, + { U("name"), U("id") }, + { U("typeName"), U("NcPropertyId") }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }) + }) }, + { U("isDeprecated"), false } + }); + + { + auto parameters = value::array(); + web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + const auto method_get_ = nmos::details::make_nc_method_descriptor(U("Get property value"), nmos::nc_object_get_method_id, U("Get"), U("NcMethodResultPropertyValue"), parameters, false); + + BST_REQUIRE_EQUAL(method_get, method_get_); + } + + const auto method_set = value_of({ + { U("description"), U("Set property value") }, + { U("id"), value_of({ + { U("level"), 1 }, + { U("index"), 2 } + }) }, + { U("name"), U("Set") }, + { U("resultDatatype"), U("NcMethodResult") }, + { U("parameters"), value_of({ + value_of({ + { U("description"), U("Property id") }, + { U("name"), U("id") }, + { U("typeName"), U("NcPropertyId") }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }), + value_of({ + { U("description"), U("Property value") }, + { U("name"), U("value") }, + { U("typeName"), value::null() }, + { U("isNullable"), true }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }) + }) }, + { U("isDeprecated"), false } + }); + + { + auto parameters = value::array(); + web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); + const auto method_set_ = nmos::details::make_nc_method_descriptor(U("Set property value"), nmos::nc_object_set_method_id, U("Set"), U("NcMethodResult"), parameters, false); + + BST_REQUIRE_EQUAL(method_set, method_set_); + } + + const auto method_get_sequence_item = value_of({ + { U("description"), U("Get sequence item") }, + { U("id"), value_of({ + { U("level"), 1 }, + { U("index"), 3 } + }) }, + { U("name"), U("GetSequenceItem") }, + { U("resultDatatype"), U("NcMethodResultPropertyValue") }, + { U("parameters"), value_of({ + value_of({ + { U("description"), U("Property id") }, + { U("name"), U("id") }, + { U("typeName"), U("NcPropertyId") }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }), + value_of({ + { U("description"), U("Index of item in the sequence") }, + { U("name"), U("index") }, + { U("typeName"), U("NcId")}, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }) + }) }, + { U("isDeprecated"), false } + }); + + { + auto parameters = value::array(); + web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + const auto method_get_sequence_item_ = nmos::details::make_nc_method_descriptor(U("Get sequence item"), nmos::nc_object_get_sequence_item_method_id, U("GetSequenceItem"), U("NcMethodResultPropertyValue"), parameters, false); + + BST_REQUIRE_EQUAL(method_get_sequence_item, method_get_sequence_item_); + } + + const auto method_set_sequence_item = value_of({ + { U("description"), U("Set sequence item value") }, + { U("id"), value_of({ + { U("level"), 1 }, + { U("index"), 4 } + }) }, + { U("name"), U("SetSequenceItem") }, + { U("resultDatatype"), U("NcMethodResult") }, + { U("parameters"), value_of({ + value_of({ + { U("description"), U("Property id") }, + { U("name"), U("id") }, + { U("typeName"), U("NcPropertyId") }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }), + value_of({ + { U("description"), U("Index of item in the sequence") }, + { U("name"), U("index") }, + { U("typeName"), U("NcId") }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }), + value_of({ + { U("description"), U("Value") }, + { U("name"), U("value") }, + { U("typeName"), value::null() }, + { U("isNullable"), true }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }) + }) }, + { U("isDeprecated"), false } + }); + + { + auto parameters = value::array(); + web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); + const auto method_set_sequence_item_ = nmos::details::make_nc_method_descriptor(U("Set sequence item value"), nmos::nc_object_set_sequence_item_method_id, U("SetSequenceItem"), U("NcMethodResult"), parameters, false); + + BST_REQUIRE_EQUAL(method_set_sequence_item, method_set_sequence_item_); + } + + const auto method_add_sequence_item = value_of({ + { U("description"), U("Add item to sequence") }, + { U("id"), value_of({ + { U("level"), 1 }, + { U("index"), 5 } + }) }, + { U("name"), U("AddSequenceItem") }, + { U("resultDatatype"), U("NcMethodResultId") }, + { U("parameters"), value_of({ + value_of({ + { U("description"), U("Property id") }, + { U("name"), U("id") }, + { U("typeName"), U("NcPropertyId") }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }), + value_of({ + { U("description"), U("Value") }, + { U("name"), U("value") }, + { U("typeName"), value::null() }, + { U("isNullable"), true }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }) + }) }, + { U("isDeprecated"), false } + }); + + { + auto parameters = value::array(); + web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); + const auto method_add_sequence_item_ = nmos::details::make_nc_method_descriptor(U("Add item to sequence"), nmos::nc_object_add_sequence_item_method_id, U("AddSequenceItem"), U("NcMethodResultId"), parameters, false); + + BST_REQUIRE_EQUAL(method_add_sequence_item, method_add_sequence_item_); + } + + const auto method_remove_sequence_item = value_of({ + { U("description"), U("Delete sequence item") }, + { U("id"), value_of({ + { U("level"), 1 }, + { U("index"), 6 } + }) }, + { U("name"), U("RemoveSequenceItem") }, + { U("resultDatatype"), U("NcMethodResult") }, + { U("parameters"), value_of({ + value_of({ + { U("description"), U("Property id") }, + { U("name"), U("id") }, + { U("typeName"), U("NcPropertyId") }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }), + value_of({ + { U("description"), U("Index of item in the sequence") }, + { U("name"), U("index") }, + { U("typeName"), U("NcId") }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }) + }) }, + { U("isDeprecated"), false } + }); + + { + auto parameters = value::array(); + web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + const auto method_remove_sequence_item_ = nmos::details::make_nc_method_descriptor(U("Delete sequence item"), nmos::nc_object_remove_sequence_item_method_id, U("RemoveSequenceItem"), U("NcMethodResult"), parameters, false); + + BST_REQUIRE_EQUAL(method_remove_sequence_item, method_remove_sequence_item_); + } + + const auto method_get_sequence_length = value_of({ + { U("description"), U("Get sequence length") }, + { U("id"), value_of({ + { U("level"), 1 }, + { U("index"), 7 } + }) }, + { U("name"), U("GetSequenceLength") }, + { U("resultDatatype"), U("NcMethodResultLength") }, + { U("parameters"), value_of({ + value_of({ + { U("description"), U("Property id") }, + { U("name"), U("id") }, + { U("typeName"), U("NcPropertyId") }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }) + }) }, + { U("isDeprecated"), false } + }); + + { + auto parameters = value::array(); + web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + const auto method_get_sequence_length_ = nmos::details::make_nc_method_descriptor(U("Get sequence length"), nmos::nc_object_get_sequence_length_method_id, U("GetSequenceLength"), U("NcMethodResultLength"), parameters, false); + + BST_REQUIRE_EQUAL(method_get_sequence_length, method_get_sequence_length_); + } + + const auto event_property_changed = value_of({ + { U("description"), U("Property changed event") }, + { U("id"), value_of({ + { U("level"), 1 }, + { U("index"), 1 } + }) }, + { U("name"), U("PropertyChanged") }, + { U("eventDatatype"), U("NcPropertyChangedEventData") }, + { U("isDeprecated"), false } + }); + + const auto event_property_changed_ = nmos::details::make_nc_event_descriptor(U("Property changed event"), nmos::nc_object_property_changed_event_id, U("PropertyChanged"), U("NcPropertyChangedEventData"), false); + BST_REQUIRE_EQUAL(event_property_changed, event_property_changed_); + + const auto nc_object_class = value_of({ + { U("description"), U("NcObject class descriptor") }, + { U("classId"), value_of({ + { 1 } + }) }, + { U("name"), U("NcObject") }, + { U("fixedRole"), value::null() }, + { U("properties"), value_of({ + property_class_id, + property_oid, + property_constant_oid, + property_owner, + property_role, + property_user_label, + property_touchpoints, + property_runtime_property_constraints + }) }, + { U("methods"), value_of({ + method_get, + method_set, + method_get_sequence_item, + method_set_sequence_item, + method_add_sequence_item, + method_remove_sequence_item, + method_get_sequence_length + }) }, + { U("events"), value_of({ + event_property_changed + }) } + }); + const auto nc_object_class_ = nmos::details::make_nc_class_descriptor(U("NcObject class descriptor"), nmos::nc_object_class_id, U("NcObject"), value::null(), nmos::make_nc_object_properties(), nmos::make_nc_object_methods(), nmos::make_nc_object_events()); + BST_REQUIRE_EQUAL(nc_object_class, nc_object_class_); +} + +BST_TEST_CASE(testNcBlockMemberDescriptor) +{ + using web::json::value_of; + using web::json::value; + + // NcBlockMemberDescriptor + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcBlockMemberDescriptor.html + const auto nc_datatype_descriptor = value_of({ + { U("description"), U("Descriptor which is specific to a block member") }, + { U("name"), U("NcBlockMemberDescriptor") }, + { U("type"), 2 }, + { U("fields"), value_of({ + value_of({ + { U("description"), U("Role of member in its containing block") }, + { U("name"), U("role") }, + { U("typeName"), U("NcString") }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }), + value_of({ + { U("description"), U("OID of member") }, + { U("name"), U("oid") }, + { U("typeName"), U("NcOid") }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }), + value_of({ + { U("description"), U("TRUE iff member's OID is hardwired into device") }, + { U("name"), U("constantOid") }, + { U("typeName"), U("NcBoolean") }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }), + value_of({ + { U("description"), U("Class ID") }, + { U("name"), U("classId") }, + { U("typeName"), U("NcClassId") }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }), + value_of({ + { U("description"), U("User label") }, + { U("name"), U("userLabel") }, + { U("typeName"), U("NcString") }, + { U("isNullable"), true }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }), + value_of({ + { U("description"), U("Containing block's OID") }, + { U("name"), U("owner") }, + { U("typeName"), U("NcOid") }, + { U("isNullable"), false }, + { U("isSequence"), false }, + { U("constraints"), value::null() } + }) + }) }, + { U("parentType"), U("NcDescriptor") }, + { U("constraints"), value::null() } + }); + + auto fields = value::array(); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Role of member in its containing block"), nmos::fields::nc::role, U("NcString"), false, false, value::null())); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("OID of member"), nmos::fields::nc::oid, U("NcOid"), false, false, value::null())); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("TRUE iff member's OID is hardwired into device"), nmos::fields::nc::constant_oid, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Class ID"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("User label"), nmos::fields::nc::user_label, U("NcString"), true, false, value::null())); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Containing block's OID"), nmos::fields::nc::owner, U("NcOid"), false, false, value::null())); + const auto nc_datatype_descriptor_ = nmos::details::make_nc_datatype_descriptor_struct(U("Descriptor which is specific to a block member"), U("NcBlockMemberDescriptor"), fields, U("NcDescriptor"), value::null()); + + BST_REQUIRE_EQUAL(nc_datatype_descriptor, nc_datatype_descriptor_); +} + +BST_TEST_CASE(testNcClassId) +{ + using web::json::value_of; + using web::json::value; + + // NcClassId + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassId.html + const auto nc_class_id = value_of({ + { U("description"), U("Sequence of class ID fields") }, + { U("name"), U("NcClassId") }, + { U("type"), 1 }, + { U("parentType"), U("NcInt32") }, + { U("isSequence"), true }, + { U("constraints"), value::null() } + }); + const auto nc_class_id_ = nmos::details::make_nc_datatype_typedef(U("Sequence of class ID fields"), U("NcClassId"), true, U("NcInt32"), value::null()); + + BST_REQUIRE_EQUAL(nc_class_id, nc_class_id_); +} + +BST_TEST_CASE(testNcDeviceGenericState) +{ + using web::json::value_of; + using web::json::value; + + // NcDeviceGenericState + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceGenericState.html + const auto nc_device_generic_state = value_of({ + { U("description"), U("Device generic operational state") }, + { U("name"), U("NcDeviceGenericState") }, + { U("type"), 3 }, + { U("items"), value_of({ + value_of({ + { U("description"), U("Unknown") }, + { U("name"), U("Unknown") }, + { U("value"), 0 } + }), + value_of({ + { U("description"), U("Normal operation") }, + { U("name"), U("NormalOperation") }, + { U("value"), 1 } + }), + value_of({ + { U("description"), U("Device is initializing") }, + { U("name"), U("Initializing") }, + { U("value"), 2 } + }), + value_of({ + { U("description"), U("Device is performing a software or firmware update") }, + { U("name"), U("Updating") }, + { U("value"), 3 } + }), + value_of({ + { U("description"), U("Device is experiencing a licensing error") }, + { U("name"), U("LicensingError") }, + { U("value"), 4 } + }), + value_of({ + { U("description"), U("Device is experiencing an internal error") }, + { U("name"), U("InternalError") }, + { U("value"), 5 } + }) + }) }, + { U("constraints"), value::null() } + }); + + auto items = value::array(); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Unknown"), U("Unknown"), 0)); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Normal operation"), U("NormalOperation"), 1)); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Device is initializing"), U("Initializing"), 2)); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Device is performing a software or firmware update"), U("Updating"), 3)); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Device is experiencing a licensing error"), U("LicensingError"), 4)); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Device is experiencing an internal error"), U("InternalError"), 5)); + const auto nc_device_generic_state_ = nmos::details::make_nc_datatype_descriptor_enum(U("Device generic operational state"), U("NcDeviceGenericState"), items, value::null()); + + BST_REQUIRE_EQUAL(nc_device_generic_state, nc_device_generic_state_); +} + +BST_TEST_CASE(testNcDatatypeDescriptorPrimitive) +{ + using web::json::value_of; + using web::json::value; + + const auto test_primitive = value_of({ + { U("description"), U("Primitive datatype descriptor") }, + { U("name"), U("test_primitive") }, + { U("type"), 0 }, + { U("constraints"), value::null() } + }); + + const auto test_primitive_ = nmos::details::make_nc_datatype_descriptor_primitive(U("Primitive datatype descriptor"), U("test_primitive"), value::null()); + + BST_REQUIRE_EQUAL(test_primitive, test_primitive_); +} From 68defebea6655f6d417d4300a055052745728978 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 20 Sep 2023 17:37:12 +0100 Subject: [PATCH 052/250] Add more unit tests --- Development/nmos-cpp-node/main.cpp | 6 +- .../nmos-cpp-node/node_implementation.cpp | 8 +- .../nmos/control_protocol_handlers.cpp | 31 +------ Development/nmos/control_protocol_handlers.h | 9 +- Development/nmos/control_protocol_methods.cpp | 2 +- Development/nmos/control_protocol_utils.cpp | 31 ++++--- Development/nmos/control_protocol_utils.h | 12 ++- .../nmos/test/control_protocol_test.cpp | 84 +++++++++++++++++-- 8 files changed, 120 insertions(+), 63 deletions(-) diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index ee133835c..ae9d4380f 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -112,9 +112,9 @@ int main(int argc, char* argv[]) nmos::experimental::control_protocol_state control_protocol_state; if (0 <= nmos::fields::control_protocol_ws_port(node_model.settings)) { - node_implementation.on_get_control_class(nmos::make_get_control_protocol_class_handler(control_protocol_state, gate)); - node_implementation.on_get_control_datatype(nmos::make_get_control_protocol_datatype_handler(control_protocol_state, gate)); - node_implementation.on_get_control_protocol_methods(nmos::make_get_control_protocol_methods_handler(control_protocol_state, gate)); + node_implementation.on_get_control_class(nmos::make_get_control_protocol_class_handler(control_protocol_state)); + node_implementation.on_get_control_datatype(nmos::make_get_control_protocol_datatype_handler(control_protocol_state)); + node_implementation.on_get_control_protocol_methods(nmos::make_get_control_protocol_methods_handler(control_protocol_state)); } // Set up the node server diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index ee8565b55..b0f6a0e45 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -29,6 +29,7 @@ #include "nmos/format.h" #include "nmos/group_hint.h" #include "nmos/interlace_mode.h" +#include "nmos/is12_versions.h" // for IS-12 gain control #ifdef HAVE_LLDP #include "nmos/lldp_manager.h" #endif @@ -39,6 +40,7 @@ #include "nmos/node_resources.h" #include "nmos/node_server.h" #include "nmos/random.h" +#include "nmos/resource.h" // for IS-12 gain control #include "nmos/sdp_utils.h" #include "nmos/slog.h" #include "nmos/st2110_21_sender_type.h" @@ -48,10 +50,6 @@ #include "nmos/video_jxsv.h" #include "sdp/sdp.h" -// hmm, for IS-12 gain control -#include "nmos/resource.h" -#include "nmos/is12_versions.h" - // example node implementation details namespace impl { @@ -903,7 +901,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr if (!insert_resource_after(delay_millis, model.channelmapping_resources, std::move(channelmapping_output), gate)) throw node_implementation_init_exception(); } - // example of using control protocol + // example of using IS-12 control protocol if (0 <= nmos::fields::control_protocol_ws_port(model.settings)) { // example to create a non-standard Gain control class diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index 2c3fdac56..e9d70f85c 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -6,12 +6,10 @@ namespace nmos { - get_control_protocol_class_handler make_get_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) + get_control_protocol_class_handler make_get_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state) { return [&](const nc_class_id& class_id) { - slog::log(gate, SLOG_FLF) << "Retrieve control protocol control class of class_id: " << nmos::details::make_nc_class_id(class_id).serialize() << " from cache"; - auto lock = control_protocol_state.read_lock(); auto& control_classes = control_protocol_state.control_classes; @@ -24,31 +22,10 @@ namespace nmos }; } - add_control_protocol_class_handler make_add_control_protocol_class_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) - { - return [&](const nc_class_id& class_id, const experimental::control_class& control_class) - { - slog::log(gate, SLOG_FLF) << "Add control protocol control class to cache"; - - auto lock = control_protocol_state.write_lock(); - - auto& control_classes = control_protocol_state.control_classes; - if (control_classes.end() == control_classes.find(class_id)) - { - return false; - } - - control_classes[class_id] = control_class; - return true; - }; - } - - get_control_protocol_datatype_handler make_get_control_protocol_datatype_handler(nmos::experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) + get_control_protocol_datatype_handler make_get_control_protocol_datatype_handler(nmos::experimental::control_protocol_state& control_protocol_state) { return [&](const nmos::nc_name& name) { - slog::log(gate, SLOG_FLF) << "Retrieve control protocol datatype of name: " << name << " from cache"; - auto lock = control_protocol_state.read_lock(); auto found = control_protocol_state.datatypes.find(name); @@ -60,12 +37,10 @@ namespace nmos }; } - get_control_protocol_methods_handler make_get_control_protocol_methods_handler(experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate) + get_control_protocol_methods_handler make_get_control_protocol_methods_handler(experimental::control_protocol_state& control_protocol_state) { return [&]() { - slog::log(gate, SLOG_FLF) << "Retrieve all control protocol method handlers from cache"; - std::map methods; auto lock = control_protocol_state.read_lock(); diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index ce4c03b45..4f328ac3b 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -45,16 +45,13 @@ namespace nmos typedef std::function()> get_control_protocol_methods_handler; // construct callback to retrieve a specific control protocol class - get_control_protocol_class_handler make_get_control_protocol_class_handler(experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); - - // construct callback to add control protocol class - add_control_protocol_class_handler make_add_control_protocol_class_handler(experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); + get_control_protocol_class_handler make_get_control_protocol_class_handler(experimental::control_protocol_state& control_protocol_state); // construct callback to retrieve a specific datatype - get_control_protocol_datatype_handler make_get_control_protocol_datatype_handler(experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); + get_control_protocol_datatype_handler make_get_control_protocol_datatype_handler(experimental::control_protocol_state& control_protocol_state); // construct callback to retrieve all method handlers - get_control_protocol_methods_handler make_get_control_protocol_methods_handler(experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); + get_control_protocol_methods_handler make_get_control_protocol_methods_handler(experimental::control_protocol_state& control_protocol_state); } #endif diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index 6d8a93dea..87c43054c 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -323,7 +323,7 @@ namespace nmos } // NcBlock methods implementation - // Get descriptors of members of the block + // Gets descriptors of members of the block web::json::value get_member_descriptors(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 5fa97bd8c..a6a43e07f 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -32,6 +32,12 @@ namespace nmos return details::is_control_class(nc_block_class_id, class_id); } + // is the given class_id a NcWorker + bool is_nc_worker(const nc_class_id& class_id) + { + return details::is_control_class(nc_worker_class_id, class_id); + } + // is the given class_id a NcManager bool is_nc_manager(const nc_class_id& class_id) { @@ -50,6 +56,19 @@ namespace nmos return details::is_control_class(nc_class_manager_class_id, class_id); } + // construct NcClassId + nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const std::vector& suffix) + { + nc_class_id class_id = prefix; + class_id.push_back(authority_key); + class_id.insert(class_id.end(), suffix.begin(), suffix.end()); + return class_id; + } + nc_class_id make_nc_class_id(const nc_class_id& prefix, const std::vector& suffix) + { + return make_nc_class_id(prefix, 0, suffix); + } + // find control class property (NcPropertyDescriptor) web::json::value find_property(const nc_property_id& property_id, const nc_class_id& class_id_, get_control_protocol_class_handler get_control_protocol_class) { @@ -77,23 +96,13 @@ namespace nmos return value::null(); } - // construct NcClassId - nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const std::vector& suffix) - { - nc_class_id class_id = prefix; - class_id.push_back(authority_key); - class_id.insert(class_id.end(), suffix.begin(), suffix.end()); - return class_id; - } - - // get descriptors of members of the block + // get block member descriptors void get_member_descriptors(const resources& resources, resources::iterator resource, bool recurse, web::json::array& descriptors) { if (resource->data.has_field(nmos::fields::nc::members)) { const auto& members = nmos::fields::nc::members(resource->data); - // hmm, maybe an easier way to apeend array to array for (const auto& member : members) { web::json::push_back(descriptors, member); diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index 2ab06e9e7..da506c8fc 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -9,6 +9,9 @@ namespace nmos // is the given class_id a NcBlock bool is_nc_block(const nc_class_id& class_id); + // is the given class_id a NcWorker + bool is_nc_worker(const nc_class_id& class_id); + // is the given class_id a NcManager bool is_nc_manager(const nc_class_id& class_id); @@ -18,13 +21,14 @@ namespace nmos // is the given class_id a NcClassManager bool is_nc_class_manager(const nc_class_id& class_id); - // find control class property (NcPropertyDescriptor) - web::json::value find_property(const nc_property_id& property_id, const nc_class_id& class_id, get_control_protocol_class_handler get_control_protocol_class); - // construct NcClassId nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const std::vector& suffix); + nc_class_id make_nc_class_id(const nc_class_id& prefix, const std::vector& suffix); // using default authority_key 0 + + // find control class property (NcPropertyDescriptor) + web::json::value find_property(const nc_property_id& property_id, const nc_class_id& class_id, get_control_protocol_class_handler get_control_protocol_class); - // get descriptors of members of the block + // get block memeber descriptors void get_member_descriptors(const resources& resources, resources::iterator resource, bool recurse, web::json::array& descriptors); // find members with given role name or fragment diff --git a/Development/nmos/test/control_protocol_test.cpp b/Development/nmos/test/control_protocol_test.cpp index 8b44e335b..cff0a181c 100644 --- a/Development/nmos/test/control_protocol_test.cpp +++ b/Development/nmos/test/control_protocol_test.cpp @@ -1,12 +1,13 @@ // The first "test" is of course whether the header compiles standalone #include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_state.h" #include "nmos/control_protocol_typedefs.h" -#include "nmos/json_fields.h" +#include "nmos/control_protocol_utils.h" #include "bst/test/test.h" //////////////////////////////////////////////////////////////////////////////////////////// -BST_TEST_CASE(testNcObject) +BST_TEST_CASE(testNcClassDescriptor) { using web::json::value_of; using web::json::value; @@ -455,7 +456,7 @@ BST_TEST_CASE(testNcObject) BST_REQUIRE_EQUAL(nc_object_class, nc_object_class_); } -BST_TEST_CASE(testNcBlockMemberDescriptor) +BST_TEST_CASE(testNcDatatypeDescriptorStruct) { using web::json::value_of; using web::json::value; @@ -532,7 +533,7 @@ BST_TEST_CASE(testNcBlockMemberDescriptor) BST_REQUIRE_EQUAL(nc_datatype_descriptor, nc_datatype_descriptor_); } -BST_TEST_CASE(testNcClassId) +BST_TEST_CASE(testNcDatatypeTypedef) { using web::json::value_of; using web::json::value; @@ -552,7 +553,7 @@ BST_TEST_CASE(testNcClassId) BST_REQUIRE_EQUAL(nc_class_id, nc_class_id_); } -BST_TEST_CASE(testNcDeviceGenericState) +BST_TEST_CASE(testNcDatatypeDescriptorEnum) { using web::json::value_of; using web::json::value; @@ -626,3 +627,76 @@ BST_TEST_CASE(testNcDatatypeDescriptorPrimitive) BST_REQUIRE_EQUAL(test_primitive, test_primitive_); } + +BST_TEST_CASE(testNcClassId) +{ + BST_REQUIRE_EQUAL(false, nmos::is_nc_block({ })); + BST_REQUIRE_EQUAL(false, nmos::is_nc_block({ 1 })); + BST_REQUIRE_EQUAL(false, nmos::is_nc_block({ 1, 2 })); + BST_REQUIRE_EQUAL(false, nmos::is_nc_block({ 1, 2, 0 })); + BST_REQUIRE(nmos::is_nc_block(nmos::nc_block_class_id)); + BST_REQUIRE(nmos::is_nc_block(nmos::make_nc_class_id(nmos::nc_block_class_id, { 1 }))); + + BST_REQUIRE_EQUAL(false, nmos::is_nc_worker({ })); + BST_REQUIRE_EQUAL(false, nmos::is_nc_worker({ 1 })); + BST_REQUIRE_EQUAL(false, nmos::is_nc_worker({ 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::is_nc_worker({ 1, 1, 1 })); + BST_REQUIRE(nmos::is_nc_worker(nmos::nc_worker_class_id)); + BST_REQUIRE(nmos::is_nc_worker(nmos::make_nc_class_id(nmos::nc_worker_class_id, { 1 }))); + + BST_REQUIRE_EQUAL(false, nmos::is_nc_manager({ })); + BST_REQUIRE_EQUAL(false, nmos::is_nc_manager({ 1 })); + BST_REQUIRE_EQUAL(false, nmos::is_nc_manager({ 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::is_nc_manager({ 1, 1, 1 })); + BST_REQUIRE(nmos::is_nc_manager(nmos::nc_manager_class_id)); + BST_REQUIRE(nmos::is_nc_manager(nmos::make_nc_class_id(nmos::nc_manager_class_id, { 1 }))); + + BST_REQUIRE_EQUAL(false, nmos::is_nc_device_manager({ })); + BST_REQUIRE_EQUAL(false, nmos::is_nc_device_manager({ 1 })); + BST_REQUIRE_EQUAL(false, nmos::is_nc_device_manager({ 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::is_nc_device_manager({ 1, 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::is_nc_device_manager({ 1, 3, 2 })); + BST_REQUIRE(nmos::is_nc_device_manager(nmos::nc_device_manager_class_id)); + BST_REQUIRE(nmos::is_nc_device_manager(nmos::make_nc_class_id(nmos::nc_device_manager_class_id, { 1 }))); + + BST_REQUIRE_EQUAL(false, nmos::is_nc_class_manager({ })); + BST_REQUIRE_EQUAL(false, nmos::is_nc_class_manager({ 1 })); + BST_REQUIRE_EQUAL(false, nmos::is_nc_class_manager({ 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::is_nc_class_manager({ 1, 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::is_nc_class_manager({ 1, 3, 1 })); + BST_REQUIRE(nmos::is_nc_class_manager(nmos::nc_class_manager_class_id)); + BST_REQUIRE(nmos::is_nc_class_manager(nmos::make_nc_class_id(nmos::nc_class_manager_class_id, { 1 }))); +} + +BST_TEST_CASE(testFindProperty) +{ + auto& nc_block_members_property_id = nmos::nc_block_members_property_id; + auto& nc_block_class_id = nmos::nc_block_class_id; + auto& nc_worker_class_id = nmos::nc_worker_class_id; + const auto invalid_property_id = nmos::nc_property_id(1000, 1000); + const auto invalid_class_id = nmos::nc_class_id({ 1000, 1000 }); + + nmos::experimental::control_protocol_state control_protocol_state; + auto get_control_protocol_class = nmos::make_get_control_protocol_class_handler(control_protocol_state); + + { + // valid - find members property in NcBlock + auto property = nmos::find_property(nc_block_members_property_id, nc_block_class_id, get_control_protocol_class); + BST_REQUIRE(!property.is_null()); + } + { + // invalid - find members property in NcWorker + auto property = nmos::find_property(nc_block_members_property_id, nc_worker_class_id, get_control_protocol_class); + BST_REQUIRE(property.is_null()); + } + { + // invalid - find unknown propertry in NcBlock + auto property = nmos::find_property(invalid_property_id, nc_block_class_id, get_control_protocol_class); + BST_REQUIRE(property.is_null()); + } + { + // invalid - find unknown property in unknown class + auto property = nmos::find_property(invalid_property_id, invalid_class_id, get_control_protocol_class); + BST_REQUIRE(property.is_null()); + } +} From f773372b085bddcc55d22901203803cdec5673cb Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 21 Sep 2023 13:53:31 +0100 Subject: [PATCH 053/250] Add NcPropertyConstraintsNumber, NcPropertyConstraintsString, NcParameterConstraintsNumber, NcParameterConstraintsString datatypes --- .../nmos-cpp-node/node_implementation.cpp | 6 +- .../nmos/control_protocol_resource.cpp | 201 ++++++++++++++++++ Development/nmos/control_protocol_resource.h | 81 +++---- Development/nmos/control_protocol_state.cpp | 2 +- Development/nmos/control_protocol_state.h | 4 +- Development/nmos/control_protocol_typedefs.h | 8 + 6 files changed, 247 insertions(+), 55 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index b0f6a0e45..cc264c2fe 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -958,10 +958,8 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // Example control class properties std::vector example_control_properties = { nmos::experimental::make_control_class_property(U("Example enum property"), { 3, 1 }, enum_property, U("ExampleEnum")), - // todo constraints - nmos::experimental::make_control_class_property(U("Example string property"), { 3, 2 }, string_property, U("NcString"), false, false, false, false, value::null()), - // todo constraints - nmos::experimental::make_control_class_property(U("Example numeric property"), { 3, 3 }, number_property, U("NcUint64"), false, false, false, false, value::null()), + nmos::experimental::make_control_class_property(U("Example string property"), { 3, 2 }, string_property, U("NcString"), false, false, false, false, nmos::details::make_nc_parameter_constraints_string(10)), + nmos::experimental::make_control_class_property(U("Example numeric property"), { 3, 3 }, number_property, U("NcUint64"), false, false, false, false, nmos::details::make_nc_parameter_constraints_number(1000, 0, 1)), nmos::experimental::make_control_class_property(U("Example boolean property"), { 3, 4 }, boolean_property, U("NcBoolean")), nmos::experimental::make_control_class_property(U("Example object property"), { 3, 5 }, object_property, U("ExampleDataType")), nmos::experimental::make_control_class_property(U("Method no args invoke counter"), { 3, 6 }, method_no_args_count, U("NcUint64"), true), diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index acf369cac..53a035c56 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -1,5 +1,6 @@ #include "nmos/control_protocol_resource.h" +#include "cpprest/base_uri.h" #include "nmos/control_protocol_state.h" // for nmos::experimental::control_classes definitions #include "nmos/json_fields.h" @@ -109,6 +110,24 @@ namespace nmos { nmos::fields::nc::website, website } }); } + web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id, const web::uri& website) + { + using web::json::value; + + return make_nc_manufacturer(name, organization_id, value::string(website.to_string())); + } + web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id) + { + using web::json::value; + + return make_nc_manufacturer(name, organization_id, value::null()); + } + web::json::value make_nc_manufacturer(const utility::string_t& name) + { + using web::json::value; + + return make_nc_manufacturer(name, value::null(), value::null()); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncproduct // brand_name can be null @@ -128,6 +147,33 @@ namespace nmos { nmos::fields::nc::description, description } }); } + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const utility::string_t& brand_name, const nc_uuid& uuid, const utility::string_t& description) + { + using web::json::value; + + return make_nc_product(name, key, revision_level, value::string(brand_name), value::string(uuid), value::string(description)); + } + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const utility::string_t& brand_name, const nc_uuid& uuid) + { + using web::json::value; + + return make_nc_product(name, key, revision_level, value::string(brand_name), value::string(uuid), value::null()); + } + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const utility::string_t& brand_name) + { + using web::json::value; + + return make_nc_product(name, key, revision_level, value::string(brand_name), value::null(), value::null()); + } + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level) + { + using web::json::value; + + return make_nc_product(name, key, revision_level, value::null(), value::null(), value::null()); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdeviceoperationalstate // device_specific_details can be null @@ -197,6 +243,18 @@ namespace nmos return make_nc_class_descriptor(value::string(description), class_id, name, fixed_role, properties, methods, events); } + web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) + { + using web::json::value; + + return make_nc_class_descriptor(value::string(description), class_id, name, value::string(fixed_role), properties, methods, events); + } + web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) + { + using web::json::value; + + return make_nc_class_descriptor(value::string(description), class_id, name, value::null(), properties, methods, events); + } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncenumitemdescriptor // description can be null @@ -443,6 +501,149 @@ namespace nmos return make_nc_datatype_typedef(value::string(description), name, is_sequence, parent_type, constraints); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraints + web::json::value make_nc_property_constraints(const nc_property_id& property_id, const web::json::value& default_value) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::property_id, make_nc_property_id(property_id) }, + { nmos::fields::nc::default_value, default_value } + }); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsnumber + web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, const web::json::value& default_value, const web::json::value& maximum, const web::json::value& minimum, const web::json::value& step) + { + using web::json::value; + + auto data = make_nc_property_constraints(property_id, default_value); + data[nmos::fields::nc::maximum] = maximum; + data[nmos::fields::nc::minimum] = minimum; + data[nmos::fields::nc::step] = step; + + return data; + } + web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t default_value, uint64_t maximum, uint64_t minimum, uint64_t step) + { + using web::json::value; + + return make_nc_property_constraints_number(property_id, value(default_value), value(maximum), value(minimum), value(step)); + } + web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t maximum, uint64_t minimum, uint64_t step) + { + using web::json::value; + + return make_nc_property_constraints_number(property_id, value::null(), maximum, minimum, step); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsstring + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const web::json::value& default_value, const web::json::value& max_characters, const web::json::value& pattern) + { + using web::json::value; + + auto data = make_nc_property_constraints(property_id, default_value); + data[nmos::fields::nc::max_characters] = max_characters; + data[nmos::fields::nc::pattern] = pattern; + + return data; + } + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern) + { + using web::json::value; + + return make_nc_property_constraints_string(property_id, value::string(default_value), max_characters, value::string(pattern)); + } + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters, const nc_regex& pattern) + { + using web::json::value; + + return make_nc_property_constraints_string(property_id, value::null(), max_characters, value::string(pattern)); + } + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters) + { + using web::json::value; + + return make_nc_property_constraints_string(property_id, value::null(), max_characters, value::null()); + } + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const nc_regex& pattern) + { + using web::json::value; + + return make_nc_property_constraints_string(property_id, value::null(), value::null(), value::string(pattern)); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraints + web::json::value make_nc_parameter_constraints(const web::json::value& default_value) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::default_value, default_value } + }); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber + web::json::value make_nc_parameter_constraints_number(const web::json::value& default_value, const web::json::value& maximum, const web::json::value& minimum, const web::json::value& step) + { + using web::json::value; + + auto data = make_nc_parameter_constraints(default_value); + data[nmos::fields::nc::maximum] = maximum; + data[nmos::fields::nc::minimum] = minimum; + data[nmos::fields::nc::step] = step; + + return data; + } + web::json::value make_nc_parameter_constraints_number(uint64_t default_value, uint64_t maximum, uint64_t minimum, uint64_t step) + { + using web::json::value; + + return make_nc_parameter_constraints_number(value(default_value), value(maximum), value(minimum), value(step)); + } + web::json::value make_nc_parameter_constraints_number(uint64_t maximum, uint64_t minimum, uint64_t step) + { + using web::json::value; + + return make_nc_parameter_constraints_number(value::null(), maximum, minimum, step); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring + web::json::value make_nc_parameter_constraints_string(const web::json::value& default_value, const web::json::value& max_characters, const web::json::value& pattern) + { + using web::json::value; + + auto data = make_nc_parameter_constraints(default_value); + data[nmos::fields::nc::max_characters] = max_characters; + data[nmos::fields::nc::pattern] = pattern; + + return data; + } + web::json::value make_nc_parameter_constraints_string(const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern) + { + using web::json::value; + + return make_nc_parameter_constraints_string(value::string(default_value), max_characters, value::string(pattern)); + } + web::json::value make_nc_parameter_constraints_string(uint32_t max_characters, const nc_regex& pattern) + { + using web::json::value; + + return make_nc_parameter_constraints_string(value::null(), max_characters, value::string(pattern)); + } + web::json::value make_nc_parameter_constraints_string(uint32_t max_characters) + { + using web::json::value; + + return make_nc_parameter_constraints_string(value::null(), max_characters, value::null()); + } + web::json::value make_nc_parameter_constraints_string(const nc_regex& pattern) + { + using web::json::value; + + return make_nc_parameter_constraints_string(value::null(), value::null(), value::string(pattern)); + } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 2479a7f9f..af832ab16 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -10,6 +10,7 @@ namespace web { class value; } + class uri; } namespace nmos @@ -44,111 +45,95 @@ namespace nmos nc_class_id parse_nc_class_id(const web::json::array& class_id); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanufacturer - web::json::value make_nc_manufacturer(const utility::string_t& name, const web::json::value& organization_id = web::json::value::null(), const web::json::value& website = web::json::value::null()); + web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id, const web::uri& website); + web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id); + web::json::value make_nc_manufacturer(const utility::string_t& name); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncproduct - // brand_name can be null - // uuid can be null - // description can be null web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, - const web::json::value& brand_name = web::json::value::null(), const web::json::value& uuid = web::json::value::null(), const web::json::value& description = web::json::value::null()); + const utility::string_t& brand_name, const nc_uuid& uuid, const utility::string_t& description); + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const utility::string_t& brand_name, const nc_uuid& uuid); + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const utility::string_t& brand_name); + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdeviceoperationalstate // device_specific_details can be null web::json::value make_nc_device_operational_state(nc_device_generic_state::state generic_state, const web::json::value& device_specific_details); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdescriptor - // description can be null - web::json::value make_nc_descriptor(const web::json::value& description); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblockmemberdescriptor - // description can be null - // user_label can be null - web::json::value make_nc_block_member_descriptor(const web::json::value& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const web::json::value& user_label, nc_oid owner); web::json::value make_nc_block_member_descriptor(const utility::string_t& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const utility::string_t& user_label, nc_oid owner); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassdescriptor - // description can be null // fixedRole can be null - web::json::value make_nc_class_descriptor(const web::json::value& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); + web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); + web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncenumitemdescriptor - // description can be null - web::json::value make_nc_enum_item_descriptor(const web::json::value& description, const nc_name& name, uint16_t val); web::json::value make_nc_enum_item_descriptor(const utility::string_t& description, const nc_name& name, uint16_t val); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventdescriptor - // description can be null - // id = make_nc_event_id(level, index) - web::json::value make_nc_event_descriptor(const web::json::value& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated); web::json::value make_nc_event_descriptor(const utility::string_t& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncfielddescriptor - // description can be null - // type_name can be null // constraints can be null - web::json::value make_nc_field_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethoddescriptor - // description can be null - // id = make_nc_method_id(level, index) // sequence parameters - web::json::value make_nc_method_descriptor(const web::json::value& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated); web::json::value make_nc_method_descriptor(const utility::string_t& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterdescriptor - // description can be null - // type_name can be null - web::json::value make_nc_parameter_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); + // constraints can be null web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints); web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertydescriptor - // description can be null - // id = make_nc_property_id(level, index); - // type_name can be null // constraints can be null - web::json::value make_nc_property_descriptor(const web::json::value& description, const nc_property_id& id, const nc_name& name, const web::json::value& type_name, - bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints); web::json::value make_nc_property_descriptor(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptor - // description can be null - // constraints can be null - web::json::value make_nc_datatype_descriptor(const web::json::value& description, const nc_name& name, nc_datatype_type::type type, const web::json::value& constraints); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorenum - // description can be null // constraints can be null // items: sequence - web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints); web::json::value make_nc_datatype_descriptor_enum(const utility::string_t& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorprimitive - // description can be null // constraints can be null - web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const nc_name& name, const web::json::value& constraints); web::json::value make_nc_datatype_descriptor_primitive(const utility::string_t& description, const nc_name& name, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorstruct - // description can be null // constraints can be null // fields: sequence - // parent_type can be null - web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const nc_name& name, const web::json::value& fields, const web::json::value& parent_type, const web::json::value& constraints); web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const utility::string_t& parent_type, const web::json::value& constraints); web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptortypedef - // description can be null - // constraints can be null - web::json::value make_nc_datatype_typedef(const web::json::value& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints); web::json::value make_nc_datatype_typedef(const utility::string_t& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsnumber + web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t default_value, uint64_t maximum, uint64_t minimum, uint64_t step); + web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t maximum, uint64_t minimum, uint64_t step); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsstring + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern); + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters, const nc_regex& pattern); + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters); + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const nc_regex& pattern); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber + web::json::value make_nc_parameter_constraints_number(uint64_t default_value, uint64_t maximum, uint64_t minimum, uint64_t step); + web::json::value make_nc_parameter_constraints_number(uint64_t maximum, uint64_t minimum, uint64_t step); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring + web::json::value make_nc_parameter_constraints_string(const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern); + web::json::value make_nc_parameter_constraints_string(uint32_t max_characters, const nc_regex& pattern); + web::json::value make_nc_parameter_constraints_string(uint32_t max_characters); + web::json::value make_nc_parameter_constraints_string(const nc_regex& pattern); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index f9a24eb65..80ff4254c 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -30,7 +30,7 @@ namespace nmos web::json::value events = value::array(); for (const auto& event : events_) { web::json::push_back(events, event); } - return { value::string(description), class_id, name, fixed_role, properties, methods, events, method_handlers }; + return { description, class_id, name, fixed_role, properties, methods, events, method_handlers }; } } // create control class with fixed role diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index 3be439b23..b95c26e48 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -15,7 +15,7 @@ namespace nmos { struct control_class // NcClassDescriptor { - web::json::value description; + utility::string_t description; nmos::nc_class_id class_id; nmos::nc_name name; web::json::value fixed_role; @@ -30,7 +30,7 @@ namespace nmos : class_id({ 0 }) {} - control_class(web::json::value description, nmos::nc_class_id class_id, nmos::nc_name name, web::json::value fixed_role, web::json::value properties, web::json::value methods, web::json::value events, nmos::experimental::methods method_handlers) + control_class(utility::string_t description, nmos::nc_class_id class_id, nmos::nc_name name, web::json::value fixed_role, web::json::value properties, web::json::value methods, web::json::value events, nmos::experimental::methods method_handlers) : description(std::move(description)) , class_id(std::move(class_id)) , name(std::move(name)) diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index 92e655df2..6ba68ac25 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -237,6 +237,14 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncuuid typedef utility::string_t nc_uuid; + // NcRegex + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncregex + typedef utility::string_t nc_regex; + + // NcOrganizationId + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncorganizationid + typedef int32_t nc_organization_id; + // NcClassId // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassid typedef std::vector nc_class_id; From 1bd72830ff6f24aac089341b3fe5997fd4092ced Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 21 Sep 2023 14:48:27 +0100 Subject: [PATCH 054/250] Tidy up make_nc_class_descriptor --- Development/nmos/control_protocol_methods.cpp | 6 +++- .../nmos/control_protocol_resource.cpp | 29 +++++++++---------- Development/nmos/control_protocol_resource.h | 2 -- .../nmos/test/control_protocol_test.cpp | 2 +- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index 87c43054c..d67842cad 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -455,6 +455,8 @@ namespace nmos // Get a single class descriptor web::json::value get_control_class(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) { + using web::json::value; + const auto& class_id = parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements @@ -494,7 +496,9 @@ namespace nmos inherited_class_id.pop_back(); } } - auto descriptor = details::make_nc_class_descriptor(description, class_id, name, fixed_role, properties, methods, events); + const auto descriptor = fixed_role.is_null() + ? details::make_nc_class_descriptor(description, class_id, name, properties, methods, events) + : details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), properties, methods, events); return make_control_protocol_message_response(handle, { nc_method_status::ok }, descriptor); } diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 53a035c56..590d36cc9 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -237,12 +237,6 @@ namespace nmos return data; } - web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) - { - using web::json::value; - - return make_nc_class_descriptor(value::string(description), class_id, name, fixed_role, properties, methods, events); - } web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) { using web::json::value; @@ -729,7 +723,10 @@ namespace nmos for (const auto& control_class : control_protocol_state.control_classes) { auto& ctl_class = control_class.second; - web::json::push_back(control_classes, make_nc_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.fixed_role, ctl_class.properties, ctl_class.methods, ctl_class.events)); + const auto class_description = ctl_class.fixed_role.is_null() + ? make_nc_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.properties, ctl_class.methods, ctl_class.events) + : make_nc_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.fixed_role.as_string(), ctl_class.properties, ctl_class.methods, ctl_class.events); + web::json::push_back(control_classes, class_description); } // add datatypes @@ -1166,7 +1163,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), value::null(), make_nc_object_properties(), make_nc_object_methods(), make_nc_object_events()); + return details::make_nc_class_descriptor(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), make_nc_object_properties(), make_nc_object_methods(), make_nc_object_events()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.1.html @@ -1174,7 +1171,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(U("NcBlock class descriptor"), nc_block_class_id, U("NcBlock"), value::null(), make_nc_block_properties(), make_nc_block_methods(), make_nc_block_events()); + return details::make_nc_class_descriptor(U("NcBlock class descriptor"), nc_block_class_id, U("NcBlock"), make_nc_block_properties(), make_nc_block_methods(), make_nc_block_events()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.2.html @@ -1182,7 +1179,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(U("NcWorker class descriptor"), nc_worker_class_id, U("NcWorker"), value::null(), make_nc_worker_properties(), make_nc_worker_methods(), make_nc_worker_events()); + return details::make_nc_class_descriptor(U("NcWorker class descriptor"), nc_worker_class_id, U("NcWorker"), make_nc_worker_properties(), make_nc_worker_methods(), make_nc_worker_events()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.html @@ -1190,7 +1187,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(U("NcManager class descriptor"), nc_manager_class_id, U("NcManager"), value::null(), make_nc_manager_properties(), make_nc_manager_methods(), make_nc_manager_events()); + return details::make_nc_class_descriptor(U("NcManager class descriptor"), nc_manager_class_id, U("NcManager"), make_nc_manager_properties(), make_nc_manager_methods(), make_nc_manager_events()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.1.html @@ -1198,7 +1195,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(U("NcDeviceManager class descriptor"), nc_device_manager_class_id, U("NcDeviceManager"), value::string(U("DeviceManager")), make_nc_device_manager_properties(), make_nc_device_manager_methods(), make_nc_device_manager_events()); + return details::make_nc_class_descriptor(U("NcDeviceManager class descriptor"), nc_device_manager_class_id, U("NcDeviceManager"), U("DeviceManager"), make_nc_device_manager_properties(), make_nc_device_manager_methods(), make_nc_device_manager_events()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.2.html @@ -1206,7 +1203,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(U("NcClassManager class descriptor"), nc_class_manager_class_id, U("NcClassManager"), value::string(U("ClassManager")), make_nc_class_manager_properties(), make_nc_class_manager_methods(), make_nc_class_manager_events()); + return details::make_nc_class_descriptor(U("NcClassManager class descriptor"), nc_class_manager_class_id, U("NcClassManager"), U("ClassManager"), make_nc_class_manager_properties(), make_nc_class_manager_methods(), make_nc_class_manager_events()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon @@ -1214,7 +1211,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(U("NcIdentBeacon class descriptor"), nc_ident_beacon_class_id, U("NcIdentBeacon"), value::null(), make_nc_ident_beacon_properties(), make_nc_ident_beacon_methods(), make_nc_ident_beacon_events()); + return details::make_nc_class_descriptor(U("NcIdentBeacon class descriptor"), nc_ident_beacon_class_id, U("NcIdentBeacon"), make_nc_ident_beacon_properties(), make_nc_ident_beacon_methods(), make_nc_ident_beacon_events()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor @@ -1222,7 +1219,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(U("NcReceiverMonitor class descriptor"), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), value::null(), make_nc_receiver_monitor_properties(), make_nc_receiver_monitor_methods(), make_nc_receiver_monitor_events()); + return details::make_nc_class_descriptor(U("NcReceiverMonitor class descriptor"), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), make_nc_receiver_monitor_properties(), make_nc_receiver_monitor_methods(), make_nc_receiver_monitor_events()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitorprotected @@ -1230,7 +1227,7 @@ namespace nmos { using web::json::value; - return details::make_nc_class_descriptor(U("NcReceiverMonitorProtected class descriptor"), nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), value::null(), make_nc_receiver_monitor_protected_properties(), make_nc_receiver_monitor_protected_methods(), make_nc_receiver_monitor_protected_events()); + return details::make_nc_class_descriptor(U("NcReceiverMonitorProtected class descriptor"), nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), make_nc_receiver_monitor_protected_properties(), make_nc_receiver_monitor_protected_methods(), make_nc_receiver_monitor_protected_events()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcBlockMemberDescriptor.html diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index af832ab16..1237f4787 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -66,8 +66,6 @@ namespace nmos web::json::value make_nc_block_member_descriptor(const utility::string_t& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const utility::string_t& user_label, nc_oid owner); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassdescriptor - // fixedRole can be null - web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); diff --git a/Development/nmos/test/control_protocol_test.cpp b/Development/nmos/test/control_protocol_test.cpp index cff0a181c..91a115a42 100644 --- a/Development/nmos/test/control_protocol_test.cpp +++ b/Development/nmos/test/control_protocol_test.cpp @@ -452,7 +452,7 @@ BST_TEST_CASE(testNcClassDescriptor) event_property_changed }) } }); - const auto nc_object_class_ = nmos::details::make_nc_class_descriptor(U("NcObject class descriptor"), nmos::nc_object_class_id, U("NcObject"), value::null(), nmos::make_nc_object_properties(), nmos::make_nc_object_methods(), nmos::make_nc_object_events()); + const auto nc_object_class_ = nmos::details::make_nc_class_descriptor(U("NcObject class descriptor"), nmos::nc_object_class_id, U("NcObject"), nmos::make_nc_object_properties(), nmos::make_nc_object_methods(), nmos::make_nc_object_events()); BST_REQUIRE_EQUAL(nc_object_class, nc_object_class_); } From 84f13d45907ac344c068bfd85f23003a5069ec21 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 6 Oct 2023 15:11:07 +0100 Subject: [PATCH 055/250] Fix to handle empty NCP URL path --- Development/nmos/control_protocol_ws_api.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 2fd5e1c67..c6f7d39a8 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -225,9 +225,6 @@ namespace nmos const auto& ws_ncp_path = connection_uri.path(); slog::log(gate, SLOG_FLF) << "Received websocket message: " << msg << " on connection: " << ws_ncp_path; - // extract the control protocol api version from the ws_ncp_path - const auto version = nmos::parse_api_version(web::uri::split_path(ws_ncp_path).back()); - auto websocket = websockets.right.find(connection_id); if (websockets.right.end() != websocket) { @@ -241,6 +238,11 @@ namespace nmos { try { + // extract the control protocol api version from the ws_ncp_path + if (web::uri::split_path(ws_ncp_path).empty()) { throw std::invalid_argument("empty URL"); } + const auto version = nmos::parse_api_version(web::uri::split_path(ws_ncp_path).back()); + + // convert message to JSON const auto message = value::parse(utility::conversions::to_string_t(msg)); // validate the base-message From 645ff22992b1eeeea47c4596cb1b827ac74878ac Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 6 Oct 2023 15:37:42 +0100 Subject: [PATCH 056/250] Code fix to construct nc_property_changed_event_data, thanks for @maweit reviewing --- Development/nmos/control_protocol_methods.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index d67842cad..598d73016 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -60,12 +60,13 @@ namespace nmos return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); } - const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, { parse_nc_property_id(property_id), nc_property_change_type::type::value_changed, val }); + const nc_property_changed_event_data property_changed_event_data{ parse_nc_property_id(property_id), nc_property_change_type::type::value_changed, val }; + const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, property_changed_event_data); const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); modify_resource(resources, resource->id, [&](nmos::resource& resource) { - resource.data[nmos::fields::nc::name(property)] = val; + resource.data[nmos::fields::nc::name(property)] = property_changed_event_data.value; }, notification_event); @@ -146,12 +147,13 @@ namespace nmos if (data.as_array().size() > (size_t)index) { - const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, { parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_changed, val, nc_id(index) }); + const nc_property_changed_event_data property_changed_event_data{ parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_changed, val, nc_id(index) }; + const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, property_changed_event_data); const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); modify_resource(resources, resource->id, [&](nmos::resource& resource) { - resource.data[nmos::fields::nc::name(property)][index] = val; + resource.data[nmos::fields::nc::name(property)][index] = property_changed_event_data.value; }, notification_event); @@ -197,14 +199,15 @@ namespace nmos auto& data = resource->data.at(nmos::fields::nc::name(property)); const nc_id sequence_item_index = data.is_null() ? 0 : nc_id(data.as_array().size()); - const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, { parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_added, val, sequence_item_index }); + const nc_property_changed_event_data property_changed_event_data{ parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_added, val, sequence_item_index }; + const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, property_changed_event_data); const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); modify_resource(resources, resource->id, [&](nmos::resource& resource) { auto& sequence = resource.data[nmos::fields::nc::name(property)]; if (data.is_null()) { sequence = value::array(); } - web::json::push_back(sequence, val); + web::json::push_back(sequence, property_changed_event_data.value); }, notification_event); @@ -243,7 +246,8 @@ namespace nmos if (data.as_array().size() > (size_t)index) { - const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, { parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_removed, data.as_array().at(index), nc_id(index)}); + const nc_property_changed_event_data property_changed_event_data{ parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_removed, data.as_array().at(index), nc_id(index) }; + const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, property_changed_event_data); const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); modify_resource(resources, resource->id, [&](nmos::resource& resource) From 472883ecdc8a9be8dc4c0d898521e73edc7b5f05 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 13 Oct 2023 23:11:19 +0100 Subject: [PATCH 057/250] Insert root block resource to the model will also inserting all its nested control protocol resources to the model as suggested by @maweit --- .../nmos-cpp-node/node_implementation.cpp | 37 +++++++++++++------ Development/nmos/control_protocol_resource.h | 18 +++++++++ .../nmos/control_protocol_resources.cpp | 10 ++--- Development/nmos/control_protocol_resources.h | 10 ++--- Development/nmos/control_protocol_utils.cpp | 21 +++++------ Development/nmos/control_protocol_utils.h | 6 ++- 6 files changed, 67 insertions(+), 35 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index cc264c2fe..9edcc0f2c 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -300,6 +300,27 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr return success; }; + // it is important that the model be locked before inserting, updating or deleting a resource + // and that the the node behaviour thread be notified after doing so + const auto insert_root_after = [&model, insert_resource_after](unsigned int milliseconds, nmos::control_protocol_resource& root, slog::base_gate& gate) + { + std::function insert_resources; + + insert_resources = [&milliseconds, insert_resource_after, &insert_resources, &gate](nmos::resources& resources, nmos::control_protocol_resource& resource) + { + for (auto& resource_ : resource.resources) + { + insert_resources(resources, resource_); + if (!insert_resource_after(milliseconds, resources, std::move(resource_), gate)) throw node_implementation_init_exception(); + } + }; + + auto& resources = model.control_protocol_resources; + + insert_resources(resources, root); + if (!insert_resource_after(milliseconds, resources, std::move(root), gate)) throw node_implementation_init_exception(); + }; + const auto resolve_auto = make_node_implementation_auto_resolver(model.settings); const auto set_transportfile = make_node_implementation_transportfile_setter(model.node_resources, model.settings); @@ -923,7 +944,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr auto data = nmos::details::make_nc_worker(gain_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); data[gain_value] = value::number(gain); - return nmos::resource{ nmos::is12_versions::v1_0, nmos::types::nc_object, std::move(data), true }; + return nmos::control_protocol_resource{ nmos::is12_versions::v1_0, nmos::types::nc_object, std::move(data), true }; }; // example to create a non-standard Example control class @@ -1111,7 +1132,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr data[object_sequence] = sequence; } - return nmos::resource{ nmos::is12_versions::v1_0, nmos::types::nc_object, std::move(data), true }; + return nmos::control_protocol_resource{ nmos::is12_versions::v1_0, nmos::types::nc_object, std::move(data), true }; }; @@ -1172,16 +1193,8 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // add device-manager to root-block nmos::push_back(root_block, device_manager); - // insert resources to model - if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(example_control), gate)) throw node_implementation_init_exception(); - if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(left_gain), gate)) throw node_implementation_init_exception(); - if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(right_gain), gate)) throw node_implementation_init_exception(); - if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(master_gain), gate)) throw node_implementation_init_exception(); - if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(channel_gain), gate)) throw node_implementation_init_exception(); - if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(stereo_gain), gate)) throw node_implementation_init_exception(); - if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(device_manager), gate)) throw node_implementation_init_exception(); - if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(class_manager), gate)) throw node_implementation_init_exception(); - if (!insert_resource_after(delay_millis, model.control_protocol_resources, std::move(root_block), gate)) throw node_implementation_init_exception(); + // insert control protocol resources to model + insert_root_after(delay_millis, root_block, gate); } } diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 1237f4787..7647bfd29 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -3,6 +3,7 @@ #include "cpprest/json_utils.h" #include "nmos/control_protocol_typedefs.h" +#include "nmos/resource.h" namespace web { @@ -13,6 +14,23 @@ namespace web class uri; } +namespace nmos +{ + struct control_protocol_resource : resource + { + control_protocol_resource(api_version version, nmos::type type, web::json::value&& data, nmos::id id, bool never_expire) + : resource(version, type, std::move(data), id, never_expire) + {} + + control_protocol_resource(api_version version, nmos::type type, web::json::value data, bool never_expire) + : resource(version, type, data, never_expire) + {} + + // temporary storage to hold the resources until they are moved to the model resources + std::vector resources; + }; +} + namespace nmos { namespace experimental diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index f576fd44d..df0b6b9d9 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -10,7 +10,7 @@ namespace nmos namespace details { // create block resource - resource make_block(nmos::nc_oid oid, const web::json::value& owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) + control_protocol_resource make_block(nmos::nc_oid oid, const web::json::value& owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) { using web::json::value; @@ -21,7 +21,7 @@ namespace nmos } // create block resource - resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) + control_protocol_resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) { using web::json::value; @@ -29,7 +29,7 @@ namespace nmos } // create Root block resource - resource make_root_block() + control_protocol_resource make_root_block() { using web::json::value; @@ -37,7 +37,7 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager - resource make_device_manager(nc_oid oid, const nmos::settings& settings) + control_protocol_resource make_device_manager(nc_oid oid, const nmos::settings& settings) { using web::json::value; @@ -55,7 +55,7 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager - resource make_class_manager(nc_oid oid, const nmos::experimental::control_protocol_state& control_protocol_state) + control_protocol_resource make_class_manager(nc_oid oid, const nmos::experimental::control_protocol_state& control_protocol_state) { using web::json::value; diff --git a/Development/nmos/control_protocol_resources.h b/Development/nmos/control_protocol_resources.h index c1718c0a0..99f3c588a 100644 --- a/Development/nmos/control_protocol_resources.h +++ b/Development/nmos/control_protocol_resources.h @@ -11,19 +11,19 @@ namespace nmos struct control_protocol_state; } - struct resource; + struct control_protocol_resource; // create block resource - resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), const web::json::value& members = web::json::value::array()); + control_protocol_resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), const web::json::value& members = web::json::value::array()); // create Root block resource - resource make_root_block(); + control_protocol_resource make_root_block(); // create Device manager resource - resource make_device_manager(nc_oid oid, const nmos::settings& settings); + control_protocol_resource make_device_manager(nc_oid oid, const nmos::settings& settings); // create Class manager resource - resource make_class_manager(nc_oid oid, const nmos::experimental::control_protocol_state& control_protocol_state); + control_protocol_resource make_class_manager(nc_oid oid, const nmos::experimental::control_protocol_state& control_protocol_state); } #endif diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index a6a43e07f..0ab690021 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -221,21 +221,20 @@ namespace nmos } } - // add block (NcBlock) to other block (NcBlock) - bool push_back(resource& parent_block, const resource& child_block) + // push control protocol resource into other control protocol NcBlock resource + void push_back(control_protocol_resource& nc_block_resource, const control_protocol_resource& resource) { using web::json::value; - auto& parent = parent_block.data; - const auto& child = child_block.data; + auto& parent = nc_block_resource.data; + const auto& child = resource.data; - if (is_nc_block(details::parse_nc_class_id(nmos::fields::nc::class_id(parent))) ) - { - web::json::push_back(parent[nmos::fields::nc::members], - details::make_nc_block_member_descriptor(nmos::fields::description(child), nmos::fields::nc::role(child), nmos::fields::nc::oid(child), nmos::fields::nc::constant_oid(child), details::parse_nc_class_id(nmos::fields::nc::class_id(child)), nmos::fields::nc::user_label(child), nmos::fields::nc::oid(parent))); - return true; - } - return false; + if (!is_nc_block(details::parse_nc_class_id(nmos::fields::nc::class_id(parent)))) throw std::logic_error("non-NcBlock cannot be nested"); + + web::json::push_back(parent[nmos::fields::nc::members], + details::make_nc_block_member_descriptor(nmos::fields::description(child), nmos::fields::nc::role(child), nmos::fields::nc::oid(child), nmos::fields::nc::constant_oid(child), details::parse_nc_class_id(nmos::fields::nc::class_id(child)), nmos::fields::nc::user_label(child), nmos::fields::nc::oid(parent))); + + nc_block_resource.resources.push_back(resource); } // modify a resource, and insert notification event to all subscriptions diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index da506c8fc..ef69f1673 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -6,6 +6,8 @@ namespace nmos { + struct control_protocol_resource; + // is the given class_id a NcBlock bool is_nc_block(const nc_class_id& class_id); @@ -37,8 +39,8 @@ namespace nmos // find members with given class id void find_members_by_class_id(const resources& resources, resources::iterator resource, const nc_class_id& class_id, bool include_derived, bool recurse, web::json::array& descriptors); - // add block (NcBlock) to other block (NcBlock) - bool push_back(resource& parent_block, const resource& child_block); + // push control protocol resource into other control protocol NcBlock resource + void push_back(control_protocol_resource& nc_block_resource, const control_protocol_resource& resource); // modify a resource, and insert notification event to all subscriptions bool modify_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event); From 094f5684b86d4424bbb1547c9ac0a0ed487d5d71 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Sat, 14 Oct 2023 00:02:18 +0100 Subject: [PATCH 058/250] Add IS-12 to Readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 050394924..b8a917cba 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,14 @@ This repository contains an implementation of the [AMWA Networked Media Open Spe - [AMWA IS-07 NMOS Event & Tally Specification](https://specs.amwa.tv/is-07/) - [AMWA IS-08 NMOS Audio Channel Mapping Specification](https://specs.amwa.tv/is-08/) - [AMWA IS-09 NMOS System Parameters Specification](https://specs.amwa.tv/is-09/) (originally defined in JT-NM TR-1001-1:2018 Annex A) +- [AMWA IS-12 AMWA IS-12 NMOS Control Protocol](https://specs.amwa.tv/is-12/) - [AMWA BCP-002-01 NMOS Grouping Recommendations - Natural Grouping](https://specs.amwa.tv/bcp-002-01/) - [AMWA BCP-002-02 NMOS Asset Distinguishing Information](https://specs.amwa.tv/bcp-002-02/) - [AMWA BCP-003-01 Secure Communication in NMOS Systems](https://specs.amwa.tv/bcp-003-01/) - [AMWA BCP-004-01 NMOS Receiver Capabilities](https://specs.amwa.tv/bcp-004-01/) - [AMWA BCP-006-01 NMOS With JPEG XS](https://specs.amwa.tv/bcp-006-01/) +- [AMWA MS-05-01 NMOS Control Architecture](https://specs.amwa.tv/ms-05-01/) +- [AMWA MS-05-02 NMOS Control Framework](https://specs.amwa.tv/ms-05-02/) For more information about AMWA, NMOS and the Networked Media Incubator, please refer to . @@ -112,6 +115,7 @@ The implementation is designed to be extended. Development is ongoing, following Recent activity on the project (newest first): +- Added support for the IS-12 NMOS Control Protocol - Added support for HSTS and OCSP stapling - Added support for BCP-006-01 v1.0-dev, which can be demonstrated with **nmos-cpp-node** by using `"video_type": "video/jxsv"` - Updates to the GitHub Actions build-test workflow for better coverage of platforms and to include unicast DNS-SD tests From 3ff45a406fb32678cc6db6e97744c7f35727dc3a Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 18 Oct 2023 22:33:40 +0100 Subject: [PATCH 059/250] Add tounchpoint support and link Receiver-Monitor with IS-04/IS-05 Receiver --- .../nmos-cpp-node/node_implementation.cpp | 29 +++++++- .../nmos/control_protocol_handlers.cpp | 28 ++++++++ Development/nmos/control_protocol_handlers.h | 9 ++- Development/nmos/control_protocol_methods.cpp | 8 +-- ...tocol_nmos_channel_mapping_resource_type.h | 19 +++++ .../control_protocol_nmos_resource_type.h | 23 ++++++ .../nmos/control_protocol_resource.cpp | 70 +++++++++++++++++-- Development/nmos/control_protocol_resource.h | 9 +++ .../nmos/control_protocol_resources.cpp | 15 ++++ Development/nmos/control_protocol_resources.h | 8 +++ Development/nmos/control_protocol_typedefs.h | 66 ++++++++++++++++- Development/nmos/control_protocol_utils.cpp | 27 ++++++- Development/nmos/control_protocol_utils.h | 7 +- Development/nmos/control_protocol_ws_api.cpp | 4 +- 14 files changed, 299 insertions(+), 23 deletions(-) create mode 100644 Development/nmos/control_protocol_nmos_channel_mapping_resource_type.h create mode 100644 Development/nmos/control_protocol_nmos_resource_type.h diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 9edcc0f2c..e89a9eaa0 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -40,7 +40,6 @@ #include "nmos/node_resources.h" #include "nmos/node_server.h" #include "nmos/random.h" -#include "nmos/resource.h" // for IS-12 gain control #include "nmos/sdp_utils.h" #include "nmos/slog.h" #include "nmos/st2110_21_sender_type.h" @@ -939,7 +938,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr control_protocol_state.insert(gain_control_class); } // helper function to create Gain control - auto make_gain_control = [&gain_value, &gain_control_class_id](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, float gain = 0.0, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null()) + auto make_gain_control = [&gain_value, &gain_control_class_id](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), float gain = 0.0) { auto data = nmos::details::make_nc_worker(gain_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); data[gain_value] = value::number(gain); @@ -1184,6 +1183,26 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr { make_example_datatype(example_enum::Alpha, U("example"), 50, false), make_example_datatype(example_enum::Gamma, U("different"), 75, true) } ); + // example receiver-monitor(s) + { + int count = 0; + for (int index = 0; index < how_many; ++index) + { + for (const auto& port : rtp_receiver_ports) + { + const auto receiver_id = impl::make_id(seed_id, nmos::types::receiver, port, index); + + utility::stringstream_t role; + role << U("monitor-") << ++count; + const auto& receiver = nmos::find_resource(model.node_resources, receiver_id); + const auto receiver_monitor = nmos::make_receiver_monitor(++oid, nmos::root_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::details::make_nc_touchpoint_nmos({nmos::ncp_nmos_resource_types::receiver, receiver_id}) } })); + + // add receiver-monitor to root-block + nmos::push_back(root_block, receiver_monitor); + } + } + } + // add example-control to root-block nmos::push_back(root_block, example_control); // add stereo-gain to root-block @@ -1545,13 +1564,17 @@ nmos::connection_activation_handler make_node_implementation_connection_activati auto handle_events_ws_message = make_node_implementation_events_ws_message_handler(model, gate); auto handle_close = nmos::experimental::make_events_ws_close_handler(model, gate); auto connection_events_activation_handler = nmos::make_connection_events_websocket_activation_handler(handle_load_ca_certificates, handle_events_ws_message, handle_close, model.settings, gate); + // this example uses this callback to update IS-12 Receiver-Monitor connection status + auto receiver_monitor_connection_activation_handler = nmos::make_receiver_monitor_connection_activation_handler(model.control_protocol_resources); - return [connection_events_activation_handler, &gate](const nmos::resource& resource, const nmos::resource& connection_resource) + return [connection_events_activation_handler, receiver_monitor_connection_activation_handler, &gate](const nmos::resource& resource, const nmos::resource& connection_resource) { const std::pair id_type{ resource.id, resource.type }; slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Activating " << id_type; connection_events_activation_handler(resource, connection_resource); + + receiver_monitor_connection_activation_handler(connection_resource); }; } diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index e9d70f85c..1e38c6a20 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -2,6 +2,7 @@ #include "nmos/control_protocol_resource.h" #include "nmos/control_protocol_state.h" +#include "nmos/control_protocol_utils.h" #include "nmos/slog.h" namespace nmos @@ -54,4 +55,31 @@ namespace nmos return methods; }; } + + control_protocol_connection_activation_handler make_receiver_monitor_connection_activation_handler(resources& resources) + { + return [&resources](const resource& connection_resource) + { + auto found = find_control_protocol_resource(resources, connection_resource.id); + if (resources.end() != found && nc_receiver_monitor_class_id == details::parse_nc_class_id(nmos::fields::nc::class_id(found->data))) + { + // update receiver-monitor's connectionStatus propertry + + auto active = nmos::fields::master_enable(nmos::fields::endpoint_active(connection_resource.data)); + + nc_property_id property_id = nc_receiver_monitor_connection_status_property_id; + web::json::value val = active ? nc_connection_status::connected : nc_connection_status::disconnected; + const nc_property_changed_event_data property_changed_event_data{ property_id, nc_property_change_type::type::value_changed, val }; + const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(found->data), nc_object_property_changed_event_id, property_changed_event_data); + const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); + + modify_control_protocol_resource(resources, found->id, [&](nmos::resource& resource) + { + resource.data[nmos::fields::nc::connection_status] = property_changed_event_data.value; + // hmm, maybe updating connectionStatusMessage, payloadStatus, and payloadStatusMessage too + + }, notification_event); + } + }; + } } diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index 4f328ac3b..bab1904f4 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -30,7 +30,7 @@ namespace nmos // callback to retrieve a control protocol datatype // this callback should not throw exceptions - typedef std::function get_control_protocol_datatype_handler; + typedef std::function get_control_protocol_datatype_handler; namespace experimental { @@ -52,6 +52,13 @@ namespace nmos // construct callback to retrieve all method handlers get_control_protocol_methods_handler make_get_control_protocol_methods_handler(experimental::control_protocol_state& control_protocol_state); + + // a control_protocol_connection_activation_handler is a notification that the active parameters for the specified (IS-05) sender/connection_sender or receiver/connection_receiver have changed + // this callback should not throw exceptions + typedef std::function control_protocol_connection_activation_handler; + + // construct callback for receiver monitor to process connection (de)activation + control_protocol_connection_activation_handler make_receiver_monitor_connection_activation_handler(nmos::resources& resources); } #endif diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index 598d73016..7c9153083 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -64,7 +64,7 @@ namespace nmos const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, property_changed_event_data); const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); - modify_resource(resources, resource->id, [&](nmos::resource& resource) + modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) { resource.data[nmos::fields::nc::name(property)] = property_changed_event_data.value; @@ -151,7 +151,7 @@ namespace nmos const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, property_changed_event_data); const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); - modify_resource(resources, resource->id, [&](nmos::resource& resource) + modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) { resource.data[nmos::fields::nc::name(property)][index] = property_changed_event_data.value; @@ -203,7 +203,7 @@ namespace nmos const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, property_changed_event_data); const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); - modify_resource(resources, resource->id, [&](nmos::resource& resource) + modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) { auto& sequence = resource.data[nmos::fields::nc::name(property)]; if (data.is_null()) { sequence = value::array(); } @@ -250,7 +250,7 @@ namespace nmos const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, property_changed_event_data); const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); - modify_resource(resources, resource->id, [&](nmos::resource& resource) + modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) { auto& sequence = resource.data[nmos::fields::nc::name(property)].as_array(); sequence.erase(index); diff --git a/Development/nmos/control_protocol_nmos_channel_mapping_resource_type.h b/Development/nmos/control_protocol_nmos_channel_mapping_resource_type.h new file mode 100644 index 000000000..8e31c96cf --- /dev/null +++ b/Development/nmos/control_protocol_nmos_channel_mapping_resource_type.h @@ -0,0 +1,19 @@ +#ifndef NMOS_CONTROL_PROTOCOL_NMOS_CHANNEL_MAPPING_RESOURCE_TYPE_H +#define NMOS_CONTROL_PROTOCOL_NMOS_CHANNEL_MAPPING_RESOURCE_TYPE_H + +#include "cpprest/basic_utils.h" +#include "nmos/string_enum.h" + +namespace nmos +{ + // resourceType + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresourcenmoschannelmapping + DEFINE_STRING_ENUM(ncp_nmos_channel_mapping_resource_type) + namespace ncp_nmos_channel_mapping_resource_types + { + const ncp_nmos_channel_mapping_resource_type input{ U("input") }; + const ncp_nmos_channel_mapping_resource_type output{ U("output") }; + } +} + +#endif diff --git a/Development/nmos/control_protocol_nmos_resource_type.h b/Development/nmos/control_protocol_nmos_resource_type.h new file mode 100644 index 000000000..436b72257 --- /dev/null +++ b/Development/nmos/control_protocol_nmos_resource_type.h @@ -0,0 +1,23 @@ +#ifndef NMOS_CONTROL_PROTOCOL_NMOS_RESOURCE_TYPE_H +#define NMOS_CONTROL_PROTOCOL_NMOS_RESOURCE_TYPE_H + +#include "cpprest/basic_utils.h" +#include "nmos/string_enum.h" + +namespace nmos +{ + // resourceType + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresourcenmos + DEFINE_STRING_ENUM(ncp_nmos_resource_type) + namespace ncp_nmos_resource_types + { + const ncp_nmos_resource_type node{ U("node") }; + const ncp_nmos_resource_type device{ U("device") }; + const ncp_nmos_resource_type source{ U("source") }; + const ncp_nmos_resource_type flow{ U("flow") }; + const ncp_nmos_resource_type sender{ U("sender") }; + const ncp_nmos_resource_type receiver{ U("receiver") }; + } +} + +#endif diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 590d36cc9..71a048923 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -605,8 +605,6 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring web::json::value make_nc_parameter_constraints_string(const web::json::value& default_value, const web::json::value& max_characters, const web::json::value& pattern) { - using web::json::value; - auto data = make_nc_parameter_constraints(default_value); data[nmos::fields::nc::max_characters] = max_characters; data[nmos::fields::nc::pattern] = pattern; @@ -638,6 +636,66 @@ namespace nmos return make_nc_parameter_constraints_string(value::null(), value::null(), value::string(pattern)); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresource + web::json::value make_nc_touchpoint_resource(const nc_touchpoint_resource& resource) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::resource_type, resource.resource_type } + }); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresourcenmos + web::json::value make_nc_touchpoint_resource_nmos(const nc_touchpoint_resource_nmos& resource) + { + using web::json::value; + + auto data = make_nc_touchpoint_resource(resource); + data[nmos::fields::nc::id] = value::string(resource.id); + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresourcenmoschannelmapping + web::json::value make_nc_touchpoint_resource_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource) + { + using web::json::value; + + auto data = make_nc_touchpoint_resource_nmos(resource); + data[nmos::fields::nc::io_id] = value::string(resource.io_id); + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpoint + web::json::value make_nc_touchpoint(const utility::string_t& context_namespace) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::context_namespace, context_namespace } + }); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointnmos + web::json::value make_nc_touchpoint_nmos(const nc_touchpoint_resource_nmos& resource) + { + auto data = make_nc_touchpoint(U("x-nmos")); + data[nmos::fields::nc::resource] = make_nc_touchpoint_resource_nmos(resource); + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointnmoschannelmapping + web::json::value make_nc_touchpoint_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource) + { + auto data = make_nc_touchpoint(U("x-nmos/channelmapping")); + data[nmos::fields::nc::resource] = make_nc_touchpoint_resource_nmos_channel_mapping(resource); + + return data; + } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { @@ -794,7 +852,7 @@ namespace nmos using web::json::value_of; return value_of({ - { nmos::fields::nc::message_type, nc_message_type::command_response }, + { nmos::fields::nc::message_type, ncp_message_type::command_response }, { nmos::fields::nc::responses, responses } }); } @@ -806,7 +864,7 @@ namespace nmos using web::json::value_of; return value_of({ - { nmos::fields::nc::message_type, nc_message_type::subscription_response }, + { nmos::fields::nc::message_type, ncp_message_type::subscription_response }, { nmos::fields::nc::subscriptions, subscriptions } }); } @@ -828,7 +886,7 @@ namespace nmos using web::json::value_of; return value_of({ - { nmos::fields::nc::message_type, nc_message_type::notification }, + { nmos::fields::nc::message_type, ncp_message_type::notification }, { nmos::fields::nc::notifications, notifications } }); } @@ -840,7 +898,7 @@ namespace nmos using web::json::value_of; return value_of({ - { nmos::fields::nc::message_type, nc_message_type::error }, + { nmos::fields::nc::message_type, ncp_message_type::error }, { nmos::fields::nc::status, method_result.status}, { nmos::fields::nc::error_message, error_message } }); diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 7647bfd29..808cfe7fc 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -150,6 +150,15 @@ namespace nmos web::json::value make_nc_parameter_constraints_string(uint32_t max_characters); web::json::value make_nc_parameter_constraints_string(const nc_regex& pattern); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpoint + web::json::value make_nc_touchpoint(const utility::string_t& context_namespace); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointnmos + web::json::value make_nc_touchpoint_nmos(const nc_touchpoint_resource_nmos& resource); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointnmoschannelmapping + web::json::value make_nc_touchpoint_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index df0b6b9d9..40a7c1c97 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -63,4 +63,19 @@ namespace nmos return{ is12_versions::v1_0, types::nc_object, std::move(data), true }; } + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor + control_protocol_resource make_receiver_monitor(nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, + nc_connection_status::status connection_status, const utility::string_t& connection_status_message, nc_payload_status::status payload_status, const utility::string_t& payload_status_message) + { + using web::json::value; + + auto data = nmos::details::make_nc_worker(nc_receiver_monitor_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); + data[nmos::fields::nc::connection_status] = value::number(connection_status); + data[nmos::fields::nc::connection_status_message] = value::string(connection_status_message); + data[nmos::fields::nc::payload_status] = value::number(payload_status); + data[nmos::fields::nc::payload_status_message] = value::string(payload_status_message); + + return{ is12_versions::v1_0, types::nc_object, std::move(data), true }; + } } diff --git a/Development/nmos/control_protocol_resources.h b/Development/nmos/control_protocol_resources.h index 99f3c588a..c17edcc8d 100644 --- a/Development/nmos/control_protocol_resources.h +++ b/Development/nmos/control_protocol_resources.h @@ -24,6 +24,14 @@ namespace nmos // create Class manager resource control_protocol_resource make_class_manager(nc_oid oid, const nmos::experimental::control_protocol_state& control_protocol_state); + + // create Receiver Monitor resource + control_protocol_resource make_receiver_monitor(nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), + nc_connection_status::status connection_status = nc_connection_status::status::undefined, + const utility::string_t& connection_status_message = U(""), + nc_payload_status::status payload_status = nc_payload_status::status::undefined, + const utility::string_t& payload_status_message = U("") + ); } #endif diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index 6ba68ac25..dc491cd45 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -3,10 +3,13 @@ #include "cpprest/basic_utils.h" #include "cpprest/json_utils.h" +#include "nmos/control_protocol_nmos_channel_mapping_resource_type.h" +#include "nmos/control_protocol_nmos_resource_type.h" namespace nmos { - namespace nc_message_type + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html + namespace ncp_message_type { enum type { @@ -20,6 +23,7 @@ namespace nmos } // Method invokation status + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethodstatus namespace nc_method_status { enum status @@ -42,7 +46,6 @@ namespace nmos property_not_implemented = 502, // Addressed property is not implemented by the addressed object not_ready = 503, // The device is not ready to handle any commands timeout = 504, // Method call did not finish within the allotted time - property_version_error = 505 // Incompatible protocol version }; } @@ -53,6 +56,7 @@ namespace nmos }; // Datatype type + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypetype namespace nc_datatype_type { enum type @@ -65,6 +69,7 @@ namespace nmos } // Device generic operational state + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicegenericstate namespace nc_device_generic_state { enum state @@ -79,6 +84,7 @@ namespace nmos } // Reset cause enum + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncresetcause namespace nc_reset_cause { enum cause @@ -312,6 +318,62 @@ namespace nmos friend bool operator!=(const nc_property_changed_event_data& lhs, const nc_property_changed_event_data& rhs) { return !(lhs == rhs); } friend bool operator<(const nc_property_changed_event_data& lhs, const nc_property_changed_event_data& rhs) { return lhs.tied() < rhs.tied(); } }; + + // NcTouchpointResource + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresource + struct nc_touchpoint_resource + { + utility::string_t resource_type; + + nc_touchpoint_resource(const utility::string_t& resource_type) + : resource_type(resource_type) + {} + + auto tied() const -> decltype(std::tie(resource_type)) { return std::tie(resource_type); } + friend bool operator==(const nc_touchpoint_resource& lhs, const nc_touchpoint_resource& rhs) { return lhs.tied() == rhs.tied(); } + friend bool operator!=(const nc_touchpoint_resource& lhs, const nc_touchpoint_resource& rhs) { return !(lhs == rhs); } + friend bool operator<(const nc_touchpoint_resource& lhs, const nc_touchpoint_resource& rhs) { return lhs.tied() < rhs.tied(); } + }; + + // NcTouchpointResourceNmos + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresourcenmos + struct nc_touchpoint_resource_nmos : nc_touchpoint_resource + { + nc_uuid id; + + nc_touchpoint_resource_nmos(const utility::string_t& resource_type, nc_uuid id) + : nc_touchpoint_resource(resource_type) + , id(id) + {} + + nc_touchpoint_resource_nmos(const ncp_nmos_resource_type& resource_type, nc_uuid id) + : nc_touchpoint_resource(resource_type.name) + , id(id) + {} + + auto tied() const -> decltype(std::tie(resource_type, id)) { return std::tie(resource_type, id); } + friend bool operator==(const nc_touchpoint_resource_nmos& lhs, const nc_touchpoint_resource_nmos& rhs) { return lhs.tied() == rhs.tied(); } + friend bool operator!=(const nc_touchpoint_resource_nmos& lhs, const nc_touchpoint_resource_nmos& rhs) { return !(lhs == rhs); } + friend bool operator<(const nc_touchpoint_resource_nmos& lhs, const nc_touchpoint_resource_nmos& rhs) { return lhs.tied() < rhs.tied(); } + }; + + // NcTouchpointResourceNmosChannelMapping + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresourcenmoschannelmapping + struct nc_touchpoint_resource_nmos_channel_mapping : nc_touchpoint_resource_nmos + { + //ncp_nmos_channel_mapping_resource_type resource_type; + nc_uuid io_id; + + nc_touchpoint_resource_nmos_channel_mapping(const ncp_nmos_channel_mapping_resource_type& resource_type, nc_uuid id, const utility::string_t& io_id) + : nc_touchpoint_resource_nmos(resource_type.name, id) + , io_id(io_id) + {} + + auto tied() const -> decltype(std::tie(resource_type, id, io_id)) { return std::tie(resource_type, id, io_id); } + friend bool operator==(const nc_touchpoint_resource_nmos_channel_mapping& lhs, const nc_touchpoint_resource_nmos_channel_mapping& rhs) { return lhs.tied() == rhs.tied(); } + friend bool operator!=(const nc_touchpoint_resource_nmos_channel_mapping& lhs, const nc_touchpoint_resource_nmos_channel_mapping& rhs) { return !(lhs == rhs); } + friend bool operator<(const nc_touchpoint_resource_nmos_channel_mapping& lhs, const nc_touchpoint_resource_nmos_channel_mapping& rhs) { return lhs.tied() < rhs.tied(); } + }; } #endif diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 0ab690021..aefb541bb 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -221,7 +221,7 @@ namespace nmos } } - // push control protocol resource into other control protocol NcBlock resource + // push a control protocol resource into other control protocol NcBlock resource void push_back(control_protocol_resource& nc_block_resource, const control_protocol_resource& resource) { using web::json::value; @@ -237,8 +237,8 @@ namespace nmos nc_block_resource.resources.push_back(resource); } - // modify a resource, and insert notification event to all subscriptions - bool modify_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event) + // modify a control protocol resource, and insert notification event to all subscriptions + bool modify_control_protocol_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event) { auto found = resources.find(id); if (resources.end() == found || !found->has_data()) return false; @@ -281,4 +281,25 @@ namespace nmos return result; } + + // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id + resources::const_iterator find_control_protocol_resource(resources& resources, const id& resource_id) + { + return find_resource_if(resources, nmos::types::nc_object, [resource_id](const nmos::resource& resource) + { + auto& touchpoints = resource.data.at(nmos::fields::nc::touchpoints); + if (!touchpoints.is_null() && touchpoints.is_array()) + { + auto& tps = touchpoints.as_array(); + auto found_tp = std::find_if(tps.begin(), tps.end(), [resource_id](const web::json::value& touchpoint) + { + auto& resource = nmos::fields::nc::resource(touchpoint); + return (resource_id == nmos::fields::nc::id(resource).as_string() + && nmos::ncp_nmos_resource_types::receiver.name == nmos::fields::nc::resource_type(resource)); + }); + return (tps.end() != found_tp); + } + return false; + }); + } } diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index ef69f1673..a62fb011c 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -42,8 +42,11 @@ namespace nmos // push control protocol resource into other control protocol NcBlock resource void push_back(control_protocol_resource& nc_block_resource, const control_protocol_resource& resource); - // modify a resource, and insert notification event to all subscriptions - bool modify_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event); + // modify a control protocol resource, and insert notification event to all subscriptions + bool modify_control_protocol_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event); + + // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id + resources::const_iterator find_control_protocol_resource(resources& resources, const id& id); } #endif diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index c6f7d39a8..3df60f751 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -251,7 +251,7 @@ namespace nmos const auto msg_type = nmos::fields::nc::message_type(message); switch (msg_type) { - case nc_message_type::command: + case ncp_message_type::command: { // validate command-message details::validate_controlprotocolapi_command_message_schema(version, message); @@ -308,7 +308,7 @@ namespace nmos }); } break; - case nc_message_type::subscription: + case ncp_message_type::subscription: { // validate subscription-message details::validate_controlprotocolapi_subscription_message_schema(version, message); From f8f5270758add47e8b966dd8eb1bc4ed04faff44 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 18 Oct 2023 22:35:39 +0100 Subject: [PATCH 060/250] Add new headers to makefile --- Development/cmake/NmosCppLibraries.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index 1fe469e1a..dd92bc22a 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -913,6 +913,8 @@ set(NMOS_CPP_NMOS_HEADERS nmos/connection_resources.h nmos/control_protocol_handlers.h nmos/control_protocol_methods.h + nmos/control_protocol_nmos_channel_mapping_resource_type.h + nmos/control_protocol_nmos_resource_type.h nmos/control_protocol_resource.h nmos/control_protocol_resources.h nmos/control_protocol_state.h From 264cfba55bb13acbcfc3fe9a4bc4f27e3fdabe1e Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 19 Oct 2023 14:49:50 +0100 Subject: [PATCH 061/250] Set IS-12 nmos resource with relevant nmos::type --- .../nmos-cpp-node/node_implementation.cpp | 4 ++-- Development/nmos/api_utils.cpp | 16 ++++++++++++++-- Development/nmos/control_protocol_handlers.cpp | 2 +- Development/nmos/control_protocol_resources.cpp | 8 ++++---- Development/nmos/control_protocol_utils.cpp | 4 ++-- Development/nmos/control_protocol_utils.h | 2 +- Development/nmos/control_protocol_ws_api.cpp | 5 +++-- Development/nmos/query_utils.cpp | 1 + Development/nmos/type.h | 14 ++++++++++---- 9 files changed, 38 insertions(+), 18 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index e89a9eaa0..b73eff13b 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -943,7 +943,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr auto data = nmos::details::make_nc_worker(gain_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); data[gain_value] = value::number(gain); - return nmos::control_protocol_resource{ nmos::is12_versions::v1_0, nmos::types::nc_object, std::move(data), true }; + return nmos::control_protocol_resource{ nmos::is12_versions::v1_0, nmos::types::nc_worker, std::move(data), true }; }; // example to create a non-standard Example control class @@ -1131,7 +1131,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr data[object_sequence] = sequence; } - return nmos::control_protocol_resource{ nmos::is12_versions::v1_0, nmos::types::nc_object, std::move(data), true }; + return nmos::control_protocol_resource{ nmos::is12_versions::v1_0, nmos::types::nc_worker, std::move(data), true }; }; diff --git a/Development/nmos/api_utils.cpp b/Development/nmos/api_utils.cpp index 69d38c5ae..3413ee509 100644 --- a/Development/nmos/api_utils.cpp +++ b/Development/nmos/api_utils.cpp @@ -157,7 +157,13 @@ namespace nmos { U("subscriptions"), nmos::types::subscription }, { U("inputs"), nmos::types::input }, { U("outputs"), nmos::types::output }, - { U("nc_object"), nmos::types::nc_object } + { U("nc_block"), nmos::types::nc_block }, + { U("nc_worker"), nmos::types::nc_worker }, + { U("nc_manager"), nmos::types::nc_manager }, + { U("nc_device_manager"), nmos::types::nc_device_manager }, + { U("nc_class_manager"), nmos::types::nc_class_manager }, + { U("nc_receiver_monitor"), nmos::types::nc_receiver_monitor }, + { U("nc_receiver_monitor_protected"), nmos::types::nc_receiver_monitor_protected } }; return types_from_resourceType.at(resourceType); } @@ -177,7 +183,13 @@ namespace nmos { nmos::types::grain, {} }, // subscription websocket grains aren't exposed via the Query API { nmos::types::input, U("inputs") }, { nmos::types::output, U("outputs") }, - { nmos::types::nc_object, U("nc_object") } + { nmos::types::nc_block, U("nc_block") }, + { nmos::types::nc_worker, U("nc_worker") }, + { nmos::types::nc_manager, U("nc_manager") }, + { nmos::types::nc_device_manager, U("nc_device_manager") }, + { nmos::types::nc_class_manager, U("nc_class_manager") }, + { nmos::types::nc_receiver_monitor, U("nc_receiver_monitor") }, + { nmos::types::nc_receiver_monitor_protected, U("nc_receiver_monitor_protected") } }; return resourceTypes_from_type.at(type); } diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index 1e38c6a20..8d7aa619c 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -60,7 +60,7 @@ namespace nmos { return [&resources](const resource& connection_resource) { - auto found = find_control_protocol_resource(resources, connection_resource.id); + auto found = find_control_protocol_resource(resources, nmos::types::nc_receiver_monitor, connection_resource.id); if (resources.end() != found && nc_receiver_monitor_class_id == details::parse_nc_class_id(nmos::fields::nc::class_id(found->data))) { // update receiver-monitor's connectionStatus propertry diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index 40a7c1c97..b1a267f7e 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -16,7 +16,7 @@ namespace nmos auto data = details::make_nc_block(nc_block_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true, members); - return{ is12_versions::v1_0, types::nc_object, std::move(data), true }; + return{ is12_versions::v1_0, types::nc_block, std::move(data), true }; } } @@ -51,7 +51,7 @@ namespace nmos auto data = details::make_nc_device_manager(oid, root_block_oid, value::string(U("Device manager")), U("The device manager offers information about the product this device is representing"), value::null(), value::null(), manufacturer, product, serial_number, value::null(), device_name, device_role, operational_state, nc_reset_cause::unknown); - return{ is12_versions::v1_0, types::nc_object, std::move(data), true }; + return{ is12_versions::v1_0, types::nc_device_manager, std::move(data), true }; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager @@ -61,7 +61,7 @@ namespace nmos auto data = details::make_nc_class_manager(oid, root_block_oid, value::string(U("Class manager")), U("The class manager offers access to control class and data type descriptors"), value::null(), value::null(), control_protocol_state); - return{ is12_versions::v1_0, types::nc_object, std::move(data), true }; + return{ is12_versions::v1_0, types::nc_class_manager, std::move(data), true }; } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor @@ -76,6 +76,6 @@ namespace nmos data[nmos::fields::nc::payload_status] = value::number(payload_status); data[nmos::fields::nc::payload_status_message] = value::string(payload_status_message); - return{ is12_versions::v1_0, types::nc_object, std::move(data), true }; + return{ is12_versions::v1_0, types::nc_receiver_monitor, std::move(data), true }; } } diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index aefb541bb..38e215f7c 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -283,9 +283,9 @@ namespace nmos } // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id - resources::const_iterator find_control_protocol_resource(resources& resources, const id& resource_id) + resources::const_iterator find_control_protocol_resource(resources& resources, type type, const id& resource_id) { - return find_resource_if(resources, nmos::types::nc_object, [resource_id](const nmos::resource& resource) + return find_resource_if(resources, type, [resource_id](const nmos::resource& resource) { auto& touchpoints = resource.data.at(nmos::fields::nc::touchpoints); if (!touchpoints.is_null() && touchpoints.is_array()) diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index a62fb011c..72580351f 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -46,7 +46,7 @@ namespace nmos bool modify_control_protocol_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event); // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id - resources::const_iterator find_control_protocol_resource(resources& resources, const id& id); + resources::const_iterator find_control_protocol_resource(resources& resources, type type, const id& id); } #endif diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 3df60f751..26948a8c9 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -118,11 +118,13 @@ namespace nmos .set_path(ws_ncp_path) .to_uri(); + const utility::string_t control_protocol_resource_path; + const bool non_persistent = false; value data = value_of({ { nmos::fields::id, nmos::make_id() }, { nmos::fields::max_update_rate_ms, 0 }, - { nmos::fields::resource_path, U('/') + nmos::resourceType_from_type(nmos::types::nc_object) }, + { nmos::fields::resource_path, control_protocol_resource_path }, { nmos::fields::params, value_of({ { U("query.rql"), U("in(id,())") } }) }, { nmos::fields::persist, non_persistent }, { nmos::fields::secure, secure }, @@ -147,7 +149,6 @@ namespace nmos const auto resource_path = nmos::fields::resource_path(subscription->data); const auto topic = resource_path + U('/'); - // source_id and flow_id are set per-message depending on the source, unlike Query WebSocket API data[nmos::fields::message] = details::make_grain({}, {}, topic); resource grain{ is12_versions::v1_0, nmos::types::grain, std::move(data), false }; diff --git a/Development/nmos/query_utils.cpp b/Development/nmos/query_utils.cpp index 672e610e6..62977ff09 100644 --- a/Development/nmos/query_utils.cpp +++ b/Development/nmos/query_utils.cpp @@ -579,6 +579,7 @@ namespace nmos } // insert 'value changed', 'sequence item added', 'sequence item changed' or 'sequence item removed' notification events into all grains whose subscriptions match the specified version, type and "pre" or "post" values + // this is used for the IS-12 propertry changed event void insert_notification_events(nmos::resources& resources, const nmos::api_version& version, const nmos::api_version& downgrade_version, const nmos::type& type, const web::json::value& pre, const web::json::value& post, const web::json::value& event) { using web::json::value; diff --git a/Development/nmos/type.h b/Development/nmos/type.h index 4e6831aa1..3c2853ebe 100644 --- a/Development/nmos/type.h +++ b/Development/nmos/type.h @@ -28,13 +28,10 @@ namespace nmos // to a subscription is managed as a sub-resource of the subscription const type grain{ U("grain") }; - // the Control Protocol API resource type, see nmos/control_protcol_resources.h - const type nc_object{ U("nc_object") }; - // all types ordered so that sub-resource types appear after super-resource types // according to the guidelines on referential integrity // see https://specs.amwa.tv/is-04/releases/v1.2.1/docs/4.1._Behaviour_-_Registration.html#referential-integrity - const std::vector all{ nmos::types::node, nmos::types::device, nmos::types::source, nmos::types::flow, nmos::types::sender, nmos::types::receiver, nmos::types::subscription, nmos::types::grain, nmos::types::nc_object }; + const std::vector all{ nmos::types::node, nmos::types::device, nmos::types::source, nmos::types::flow, nmos::types::sender, nmos::types::receiver, nmos::types::subscription, nmos::types::grain }; // the Channel Mapping API resource types, see nmos/channelmapping_resources.h const type input{ U("input") }; @@ -42,6 +39,15 @@ namespace nmos // the System API global configuration resource type, see nmos/system_resources.h const type global{ U("global") }; + + // the Control Protocol API resource type, see nmos/control_protcol_resources.h + const type nc_block{ U("nc_block") }; + const type nc_worker{ U("nc_worker") }; + const type nc_manager{ U("nc_manager") }; + const type nc_device_manager{ U("nc_device_manager") }; + const type nc_class_manager{ U("nc_class_manager") }; + const type nc_receiver_monitor{ U("nc_receiver_monitor") }; + const type nc_receiver_monitor_protected{ U("nc_receiver_monitor_protected") }; } } From ce6286507e5fadc65dfe854d31f11c631e43ce57 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 19 Oct 2023 20:07:36 +0100 Subject: [PATCH 062/250] Clean up on how to construct propertry changed event --- .../nmos/control_protocol_handlers.cpp | 19 ++++++------ Development/nmos/control_protocol_methods.cpp | 29 +++++-------------- .../nmos/control_protocol_resource.cpp | 18 +++++++++++- Development/nmos/control_protocol_resource.h | 8 ++++- Development/nmos/control_protocol_typedefs.h | 7 +++++ 5 files changed, 48 insertions(+), 33 deletions(-) diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index 8d7aa619c..f51b85cf6 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -65,20 +65,21 @@ namespace nmos { // update receiver-monitor's connectionStatus propertry - auto active = nmos::fields::master_enable(nmos::fields::endpoint_active(connection_resource.data)); + const auto active = nmos::fields::master_enable(nmos::fields::endpoint_active(connection_resource.data)); + const web::json::value val = active ? nc_connection_status::connected : nc_connection_status::disconnected; - nc_property_id property_id = nc_receiver_monitor_connection_status_property_id; - web::json::value val = active ? nc_connection_status::connected : nc_connection_status::disconnected; - const nc_property_changed_event_data property_changed_event_data{ property_id, nc_property_change_type::type::value_changed, val }; - const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(found->data), nc_object_property_changed_event_id, property_changed_event_data); - const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); + // hmm, maybe updating connectionStatusMessage, payloadStatus, and payloadStatusMessage too + + const auto propertry_changed_event = make_propertry_changed_event(nmos::fields::nc::oid(found->data), + { + { nc_receiver_monitor_connection_status_property_id, nc_property_change_type::type::value_changed, val } + }); modify_control_protocol_resource(resources, found->id, [&](nmos::resource& resource) { - resource.data[nmos::fields::nc::connection_status] = property_changed_event_data.value; - // hmm, maybe updating connectionStatusMessage, payloadStatus, and payloadStatusMessage too + resource.data[nmos::fields::nc::connection_status] = val; - }, notification_event); + }, propertry_changed_event); } }; } diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index 7c9153083..b6aad64dc 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -60,15 +60,11 @@ namespace nmos return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); } - const nc_property_changed_event_data property_changed_event_data{ parse_nc_property_id(property_id), nc_property_change_type::type::value_changed, val }; - const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, property_changed_event_data); - const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); - modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) { - resource.data[nmos::fields::nc::name(property)] = property_changed_event_data.value; + resource.data[nmos::fields::nc::name(property)] = val; - }, notification_event); + }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { parse_nc_property_id(property_id), nc_property_change_type::type::value_changed, val } })); return make_control_protocol_message_response(handle, { nc_method_status::ok }); } @@ -147,15 +143,11 @@ namespace nmos if (data.as_array().size() > (size_t)index) { - const nc_property_changed_event_data property_changed_event_data{ parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_changed, val, nc_id(index) }; - const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, property_changed_event_data); - const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); - modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) { - resource.data[nmos::fields::nc::name(property)][index] = property_changed_event_data.value; + resource.data[nmos::fields::nc::name(property)][index] = val; - }, notification_event); + }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_changed, val, nc_id(index) } })); return make_control_protocol_message_response(handle, { nc_method_status::ok }); } @@ -199,17 +191,14 @@ namespace nmos auto& data = resource->data.at(nmos::fields::nc::name(property)); const nc_id sequence_item_index = data.is_null() ? 0 : nc_id(data.as_array().size()); - const nc_property_changed_event_data property_changed_event_data{ parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_added, val, sequence_item_index }; - const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, property_changed_event_data); - const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) { auto& sequence = resource.data[nmos::fields::nc::name(property)]; if (data.is_null()) { sequence = value::array(); } - web::json::push_back(sequence, property_changed_event_data.value); + web::json::push_back(sequence, val); - }, notification_event); + }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_added, val, sequence_item_index } })); return make_control_protocol_message_response(handle, { nc_method_status::ok }, sequence_item_index); } @@ -246,16 +235,12 @@ namespace nmos if (data.as_array().size() > (size_t)index) { - const nc_property_changed_event_data property_changed_event_data{ parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_removed, data.as_array().at(index), nc_id(index) }; - const auto notification = make_control_protocol_notification(nmos::fields::nc::oid(resource->data), nc_object_property_changed_event_id, property_changed_event_data); - const auto notification_event = make_control_protocol_notification(web::json::value_of({ notification })); - modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) { auto& sequence = resource.data[nmos::fields::nc::name(property)].as_array(); sequence.erase(index); - }, notification_event); + }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_removed, nc_id(index) } })); return make_control_protocol_message_response(handle, { nc_method_status::ok }); } diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 71a048923..b374de30d 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -870,6 +870,7 @@ namespace nmos } // notification + // See https://specs.amwa.tv/ms-05-01/branches/v1.0.x/docs/Core_Mechanisms.html#notification-messages // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#notification-message-type web::json::value make_control_protocol_notification(nc_oid oid, const nc_event_id& event_id, const nc_property_changed_event_data& property_changed_event_data) { @@ -881,7 +882,7 @@ namespace nmos { nmos::fields::nc::event_data, details::make_nc_property_changed_event_data(property_changed_event_data) } }); } - web::json::value make_control_protocol_notification(const web::json::value& notifications) + web::json::value make_control_protocol_notification_message(const web::json::value& notifications) { using web::json::value_of; @@ -891,6 +892,21 @@ namespace nmos }); } + // property changed notification event + // See https://specs.amwa.tv/ms-05-01/branches/v1.0.x/docs/Core_Mechanisms.html#the-propertychanged-event + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/NcObject.html#propertychanged-event + web::json::value make_propertry_changed_event(nc_oid oid, const std::vector& property_changed_event_data_list) + { + using web::json::value; + + auto notifications = value::array(); + for (auto& property_changed_event_data : property_changed_event_data_list) + { + web::json::push_back(notifications, make_control_protocol_notification(oid, nc_object_property_changed_event_id, property_changed_event_data)); + } + return make_control_protocol_notification_message(notifications); + } + // error message // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#error-messages web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message) diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 808cfe7fc..fc4910dd5 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -193,9 +193,15 @@ namespace nmos web::json::value make_control_protocol_subscription_response(const web::json::value& subscriptions); // notification + // See https://specs.amwa.tv/ms-05-01/branches/v1.0.x/docs/Core_Mechanisms.html#notification-messages // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#notification-message-type web::json::value make_control_protocol_notification(nc_oid oid, const nc_event_id& event_id, const nc_property_changed_event_data& property_changed_event_data); - web::json::value make_control_protocol_notification(const web::json::value& notifications); + web::json::value make_control_protocol_notification_message(const web::json::value& notifications); + + // property changed notification event + // See https://specs.amwa.tv/ms-05-01/branches/v1.0.x/docs/Core_Mechanisms.html#the-propertychanged-event + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/NcObject.html#propertychanged-event + web::json::value make_propertry_changed_event(nc_oid oid, const std::vector& property_changed_event_data_list); // error message // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#error-messages diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index dc491cd45..f31ca98df 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -313,6 +313,13 @@ namespace nmos , sequence_item_index(web::json::value::null()) {} + nc_property_changed_event_data(nc_property_id property_id, nc_property_change_type::type change_type, nc_id sequence_item_index) + : property_id(std::move(property_id)) + , change_type(change_type) + , value(web::json::value::null()) + , sequence_item_index(sequence_item_index) + {} + auto tied() const -> decltype(std::tie(property_id, change_type, value, sequence_item_index)) { return std::tie(property_id, change_type, value, sequence_item_index); } friend bool operator==(const nc_property_changed_event_data& lhs, const nc_property_changed_event_data& rhs) { return lhs.tied() == rhs.tied(); } friend bool operator!=(const nc_property_changed_event_data& lhs, const nc_property_changed_event_data& rhs) { return !(lhs == rhs); } From 28187d80e351732fea11bdebe396208c6e43226b Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 26 Oct 2023 17:32:42 +0100 Subject: [PATCH 063/250] Add constraints support, see https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html --- .../nmos-cpp-node/node_implementation.cpp | 68 +++++++--- Development/nmos/control_protocol_methods.cpp | 45 +++++-- .../nmos/control_protocol_resource.cpp | 30 ++--- Development/nmos/control_protocol_resource.h | 8 +- Development/nmos/control_protocol_utils.cpp | 127 ++++++++++++++++-- Development/nmos/control_protocol_utils.h | 18 +++ Development/nmos/json_fields.h | 6 +- .../nmos/test/control_protocol_test.cpp | 66 +++++++++ 8 files changed, 313 insertions(+), 55 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index b73eff13b..e1c60fc3b 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -965,7 +965,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const web::json::field_as_string string_arg{ U("stringArg") }; const web::json::field_as_number number_arg{ U("numberArg") }; const web::json::field_as_bool boolean_arg{ U("booleanArg") }; - const web::json::field_as_bool obj_arg{ U("objArg") }; + const web::json::field_as_value obj_arg{ U("objArg") }; enum example_enum { Undefined = 0, @@ -978,8 +978,10 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // Example control class properties std::vector example_control_properties = { nmos::experimental::make_control_class_property(U("Example enum property"), { 3, 1 }, enum_property, U("ExampleEnum")), + // create "Example string property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html nmos::experimental::make_control_class_property(U("Example string property"), { 3, 2 }, string_property, U("NcString"), false, false, false, false, nmos::details::make_nc_parameter_constraints_string(10)), - nmos::experimental::make_control_class_property(U("Example numeric property"), { 3, 3 }, number_property, U("NcUint64"), false, false, false, false, nmos::details::make_nc_parameter_constraints_number(1000, 0, 1)), + // create "Example numeric property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + nmos::experimental::make_control_class_property(U("Example numeric property"), { 3, 3 }, number_property, U("NcUint64"), false, false, false, false, nmos::details::make_nc_parameter_constraints_number(0, 1000, 1)), nmos::experimental::make_control_class_property(U("Example boolean property"), { 3, 4 }, boolean_property, U("NcBoolean")), nmos::experimental::make_control_class_property(U("Example object property"), { 3, 5 }, object_property, U("ExampleDataType")), nmos::experimental::make_control_class_property(U("Method no args invoke counter"), { 3, 6 }, method_no_args_count, U("NcUint64"), true), @@ -993,19 +995,43 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr }; // Example control class method handlers + auto make_string_example_argument_constraints = []() {return nmos::details::make_nc_parameter_constraints_string(80); }; + auto make_number_example_argument_constraints = []() {return nmos::details::make_nc_parameter_constraints_number(100, 1000, 1); }; + auto example_method_with_no_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { slog::log(gate, SLOG_FLF) << "Executing the example method with no arguments"; + return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::ok }); }; - auto example_method_with_simple_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + auto example_method_with_simple_args = [&](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { - slog::log(gate, SLOG_FLF) << "Executing the example method with simple arguments"; + slog::log(gate, SLOG_FLF) << "Executing the example method with simple arguments:" + << " enum_arg: " + << enum_arg(arguments).to_int32() + << " string_arg: " + << string_arg(arguments) + << " number_arg: " + << number_arg(arguments).to_uint64() + << " boolean_arg: " + << boolean_arg(arguments); + + // example to do method arguments constraints validation + const auto string_example_argument_constraints = make_string_example_argument_constraints(); + if (!nmos::constraints_validation(arguments.at(string_arg), make_string_example_argument_constraints()) + || !nmos::constraints_validation(arguments.at(number_arg), make_number_example_argument_constraints())) + { + return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); + } + return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::ok }); }; - auto example_method_with_object_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + auto example_method_with_object_args = [&](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { - slog::log(gate, SLOG_FLF) << "Executing the example method with object arguments"; + slog::log(gate, SLOG_FLF) << "Executing the example method with object argument:" + << " obj_arg: " + << obj_arg(arguments).serialize(); + return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::ok }); }; // Example control class methods @@ -1015,8 +1041,8 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr { nmos::experimental::make_control_class_method(U("Example method with simple arguments"), { 3, 2 }, U("MethodSimpleArgs"), U("NcMethodResult"), { nmos::details::make_nc_parameter_descriptor(U("Enum example argument"), enum_arg, U("ExampleEnum"), false, false, value::null()), - nmos::details::make_nc_parameter_descriptor(U("String example argument"), string_arg, U("NcString"), false, false, value::null()), // todo constraints - nmos::details::make_nc_parameter_descriptor(U("Number example argument"), number_arg, U("NcUint64"), false, false, value::null()), // todo constraints + nmos::details::make_nc_parameter_descriptor(U("String example argument"), string_arg, U("NcString"), false, false, make_string_example_argument_constraints()), // e.g. include method property constraints + nmos::details::make_nc_parameter_descriptor(U("Number example argument"), number_arg, U("NcUint64"), false, false, make_number_example_argument_constraints()), // e.g. include method property constraints nmos::details::make_nc_parameter_descriptor(U("Boolean example argument"), boolean_arg, U("NcBoolean"), false, false, value::null()) }, false), example_method_with_simple_args @@ -1054,12 +1080,16 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr auto fields = value::array(); web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Enum property example"), enum_property, U("ExampleEnum"), false, false, value::null())); { - value constraints = value::null(); // todo constraints - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("String property example"), string_property, U("NcString"), false, false, constraints)); + // level 0: datatype constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + // use nmos::details::make_nc_parameter_constraints_string to create datatype constraints + value datatype_constraints = value::null(); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("String property example"), string_property, U("NcString"), false, false, datatype_constraints)); } { - value constraints = value::null(); // todo constraints - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Number property example"), number_property, U("NcUint64"), false, false, constraints)); + // level 0: datatype constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + // use nmos::details::make_nc_parameter_constraints_number to create datatype constraints + value datatype_constraints = value::null(); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Number property example"), number_property, U("NcUint64"), false, false, datatype_constraints)); } web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Boolean property example"), boolean_property, U("NcBoolean"), false, false, value::null())); return nmos::details::make_nc_datatype_descriptor_struct(U("Example data type"), U("ExampleDataType"), fields, value::null()); @@ -1080,7 +1110,9 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr }); }; // helper function to create Example control - auto make_example_control = [&](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, + auto make_example_control = [&](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const value& touchpoints = value::null(), + const value& runtime_property_constraints = value::null(), // level 2: runtime constraints. See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + // use of make_nc_property_constraints_stringand make_nc_property_constraints_number to create runtime constraints example_enum enum_property_ = example_enum::Undefined, const utility::string_t& string_property_ = U(""), uint64_t number_property_ = 0, @@ -1093,8 +1125,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr std::vector boolean_sequence_ = {}, std::vector enum_sequence_ = {}, std::vector number_sequence_ = {}, - std::vector object_sequence_ = {}, - const value& touchpoints = value::null(), const value& runtime_property_constraints = value::null()) + std::vector object_sequence_ = {}) { auto data = nmos::details::make_nc_worker(example_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); data[enum_property] = value::number(enum_property_); @@ -1168,6 +1199,13 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // example example-control auto example_control = make_example_control(++oid, nmos::root_block_oid, U("ExampleControl"), U("Example control worker"), U("Example control worker"), + value::null(), + value::null(), // specify the level 2: runtime constraints, see https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + // use of make_nc_property_constraints_string and make_nc_property_constraints_number to create runtime constraints + // e.g. value_of({ + // { nmos::details::make_nc_property_constraints_string({3, 2}, 10) }, + // { nmos::details::make_nc_property_constraints_number({3, 3}, 10, 100, 2) } + // }), example_enum::Undefined, U("test"), 3, diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index b6aad64dc..de2dcba4b 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -36,7 +36,7 @@ namespace nmos } // Set property value - web::json::value set(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value set(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -46,7 +46,8 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Set property: " << property_id.serialize() << " value: " << val.serialize(); // find the relevant nc_property_descriptor - const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); + const auto property_id_ = parse_nc_property_id(property_id); + const auto& property = find_property(property_id_, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); if (!property.is_null()) { if (nmos::fields::nc::is_read_only(property)) @@ -60,11 +61,21 @@ namespace nmos return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); } + // do constraints validation + if (!val.is_null()) + { + if (!constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), get_datatype_constraints(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype))) + { + return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); + } + } + + // update property modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) { resource.data[nmos::fields::nc::name(property)] = val; - }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { parse_nc_property_id(property_id), nc_property_change_type::type::value_changed, val } })); + }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::value_changed, val } })); return make_control_protocol_message_response(handle, { nc_method_status::ok }); } @@ -117,7 +128,7 @@ namespace nmos } // Set sequence item - web::json::value set_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value set_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -128,7 +139,8 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize(); // find the relevant nc_property_descriptor - const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); + const auto property_id_ = parse_nc_property_id(property_id); + const auto& property = find_property(property_id_, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); if (!property.is_null()) { auto& data = resource->data.at(nmos::fields::nc::name(property)); @@ -143,11 +155,18 @@ namespace nmos if (data.as_array().size() > (size_t)index) { + // do constraints validation + if (!constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), get_datatype_constraints(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype))) + { + return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); + } + + // update property modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) { resource.data[nmos::fields::nc::name(property)][index] = val; - }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_changed, val, nc_id(index) } })); + }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::sequence_item_changed, val, nc_id(index) } })); return make_control_protocol_message_response(handle, { nc_method_status::ok }); } @@ -165,7 +184,7 @@ namespace nmos } // Add item to sequence - web::json::value add_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value add_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -177,7 +196,8 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize(); // find the relevant nc_property_descriptor - const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); + const auto property_id_ = parse_nc_property_id(property_id); + const auto& property = find_property(property_id_, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); if (!property.is_null()) { if (!nmos::fields::nc::is_sequence(property)) @@ -192,13 +212,20 @@ namespace nmos const nc_id sequence_item_index = data.is_null() ? 0 : nc_id(data.as_array().size()); + // do constraints validation + if (!constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), get_datatype_constraints(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype))) + { + return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); + } + + // update property modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) { auto& sequence = resource.data[nmos::fields::nc::name(property)]; if (data.is_null()) { sequence = value::array(); } web::json::push_back(sequence, val); - }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_added, val, sequence_item_index } })); + }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::sequence_item_added, val, sequence_item_index } })); return make_control_protocol_message_response(handle, { nc_method_status::ok }, sequence_item_index); } diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index b374de30d..7a80aae17 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -507,28 +507,28 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsnumber - web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, const web::json::value& default_value, const web::json::value& maximum, const web::json::value& minimum, const web::json::value& step) + web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, const web::json::value& default_value, const web::json::value& minimum, const web::json::value& maximum, const web::json::value& step) { using web::json::value; auto data = make_nc_property_constraints(property_id, default_value); - data[nmos::fields::nc::maximum] = maximum; data[nmos::fields::nc::minimum] = minimum; + data[nmos::fields::nc::maximum] = maximum; data[nmos::fields::nc::step] = step; return data; } - web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t default_value, uint64_t maximum, uint64_t minimum, uint64_t step) + web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step) { using web::json::value; - return make_nc_property_constraints_number(property_id, value(default_value), value(maximum), value(minimum), value(step)); + return make_nc_property_constraints_number(property_id, value(default_value), value(minimum), value(maximum), value(step)); } - web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t maximum, uint64_t minimum, uint64_t step) + web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t minimum, uint64_t maximum, uint64_t step) { using web::json::value; - return make_nc_property_constraints_number(property_id, value::null(), maximum, minimum, step); + return make_nc_property_constraints_number(property_id, value::null(), minimum, maximum, step); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsstring @@ -578,28 +578,28 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber - web::json::value make_nc_parameter_constraints_number(const web::json::value& default_value, const web::json::value& maximum, const web::json::value& minimum, const web::json::value& step) + web::json::value make_nc_parameter_constraints_number(const web::json::value& default_value, const web::json::value& minimum, const web::json::value& maximum, const web::json::value& step) { using web::json::value; auto data = make_nc_parameter_constraints(default_value); - data[nmos::fields::nc::maximum] = maximum; data[nmos::fields::nc::minimum] = minimum; + data[nmos::fields::nc::maximum] = maximum; data[nmos::fields::nc::step] = step; return data; } - web::json::value make_nc_parameter_constraints_number(uint64_t default_value, uint64_t maximum, uint64_t minimum, uint64_t step) + web::json::value make_nc_parameter_constraints_number(uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step) { using web::json::value; - return make_nc_parameter_constraints_number(value(default_value), value(maximum), value(minimum), value(step)); + return make_nc_parameter_constraints_number(value(default_value), value(minimum), value(maximum), value(step)); } - web::json::value make_nc_parameter_constraints_number(uint64_t maximum, uint64_t minimum, uint64_t step) + web::json::value make_nc_parameter_constraints_number(uint64_t minimum, uint64_t maximum, uint64_t step) { using web::json::value; - return make_nc_parameter_constraints_number(value::null(), maximum, minimum, step); + return make_nc_parameter_constraints_number(value::null(), minimum, maximum, step); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring @@ -710,7 +710,7 @@ namespace nmos data[nmos::fields::nc::role] = value::string(role); data[nmos::fields::nc::user_label] = user_label; data[nmos::fields::nc::touchpoints] = touchpoints; - data[nmos::fields::nc::runtime_property_constraints] = runtime_property_constraints; + data[nmos::fields::nc::runtime_property_constraints] = runtime_property_constraints; // level 2 runtime constraints. See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html return data; } @@ -1690,8 +1690,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional maximum"), nmos::fields::nc::maximum, true, false, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional minimum"), nmos::fields::nc::minimum, true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional maximum"), nmos::fields::nc::maximum, true, false, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional step"), nmos::fields::nc::step, true, false, value::null())); return details::make_nc_datatype_descriptor_struct(U("Number parameter constraints class"), U("NcParameterConstraintsNumber"), fields, U("NcParameterConstraints"), value::null()); } @@ -1779,8 +1779,8 @@ namespace nmos using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional maximum"), nmos::fields::nc::maximum, true, false, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional minimum"), nmos::fields::nc::minimum, true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional maximum"), nmos::fields::nc::maximum, true, false, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional step"), nmos::fields::nc::step, true, false, value::null())); return details::make_nc_datatype_descriptor_struct(U("Number property constraints class"), U("NcPropertyConstraintsNumber"), fields, U("NcPropertyConstraints"), value::null()); } diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index fc4910dd5..1ff7e8e85 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -131,8 +131,8 @@ namespace nmos web::json::value make_nc_datatype_typedef(const utility::string_t& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsnumber - web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t default_value, uint64_t maximum, uint64_t minimum, uint64_t step); - web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t maximum, uint64_t minimum, uint64_t step); + web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step); + web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t minimum, uint64_t maximum, uint64_t step); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsstring web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern); @@ -141,8 +141,8 @@ namespace nmos web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const nc_regex& pattern); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber - web::json::value make_nc_parameter_constraints_number(uint64_t default_value, uint64_t maximum, uint64_t minimum, uint64_t step); - web::json::value make_nc_parameter_constraints_number(uint64_t maximum, uint64_t minimum, uint64_t step); + web::json::value make_nc_parameter_constraints_number(uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step); + web::json::value make_nc_parameter_constraints_number(uint64_t minimum, uint64_t maximum, uint64_t step); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring web::json::value make_nc_parameter_constraints_string(const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern); diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 38e215f7c..e03c6697f 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -3,6 +3,7 @@ #include #include #include +#include "bst/regex.h" #include "cpprest/json_utils.h" #include "nmos/control_protocol_resource.h" #include "nmos/control_protocol_state.h" @@ -24,6 +25,44 @@ namespace nmos } return control_class_id == class_id; } + + // get the runtime property constraints of a given property_id + web::json::value get_runtime_property_constraints(const nc_property_id& property_id, const web::json::value& runtime_property_constraints) + { + using web::json::value; + + if (!runtime_property_constraints.is_null()) + { + auto& runtime_prop_constraints = runtime_property_constraints.as_array(); + auto found_constraints = std::find_if(runtime_prop_constraints.begin(), runtime_prop_constraints.end(), [&property_id](const web::json::value& constraints) + { + //return nmos::fields::nc::id(property) == nmos::fields::nc::property_id(constraints); + return property_id == parse_nc_property_id(nmos::fields::nc::property_id(constraints)); + }); + + if (runtime_prop_constraints.end() != found_constraints) + { + return *found_constraints; + } + } + return value::null(); + } + + // get the datatype property constraints of a given type_name + web::json::value get_datatype_constraints(const web::json::value& type_name, get_control_protocol_datatype_handler get_control_protocol_datatype) + { + using web::json::value; + + if (!type_name.is_null()) + { + const auto& datatype = get_control_protocol_datatype(type_name.as_string()); + if (!datatype.descriptor.is_null()) // NcDatatypeDescriptor + { + return nmos::fields::nc::constraints(datatype.descriptor); + } + } + return value::null(); + } } // is the given class_id a NcBlock @@ -80,16 +119,12 @@ namespace nmos { const auto& control_class = get_control_protocol_class(class_id); auto& properties = control_class.properties.as_array(); - if (properties.size()) + auto found = std::find_if(properties.begin(), properties.end(), [&property_id](const web::json::value& property) { - for (const auto& property : properties) - { - if (property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property))) - { - return property; - } - } - } + return (property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property))); + }); + if (properties.end() != found) { return *found; } + class_id.pop_back(); } @@ -302,4 +337,78 @@ namespace nmos return false; }); } + + // constraints validation + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring + bool constraints_validation(const web::json::value& value, const web::json::value& constraints) + { + // is numeric constraints + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber + if (constraints.has_field(nmos::fields::nc::step) && !nmos::fields::nc::step(constraints).is_null()) + { + if (!value.is_integer()) { return false; } + + const auto step = nmos::fields::nc::step(constraints).as_double(); + if (step <= 0) { return false; } + + const auto value_double = value.as_double(); + if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) + { + auto min = nmos::fields::nc::minimum(constraints).as_double(); + if (0 != std::fmod(value_double - min, step)) { return false; } + } + else if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) + { + auto max = nmos::fields::nc::maximum(constraints).as_double(); + if (0 != std::fmod(max - value_double, step)) { return false; } + } + else + { + if (0 != std::fmod(value_double, step)) { return false; } + } + } + if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) + { + if (!value.is_integer() || value.as_double() < nmos::fields::nc::minimum(constraints).as_double()) { return false; } + } + if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) + { + if (!value.is_integer() || value.as_double() > nmos::fields::nc::maximum(constraints).as_double()) { return false; } + } + + // is string constraints + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring + if (constraints.has_field(nmos::fields::nc::max_characters) && !constraints.at(nmos::fields::nc::max_characters).is_null()) + { + const auto max_characters = nmos::fields::nc::max_characters(constraints); + if (!value.is_string() || value.as_string().length() > max_characters) { return false; } + } + if (constraints.has_field(nmos::fields::nc::pattern) && !constraints.at(nmos::fields::nc::pattern).is_null()) + { + if (!value.is_string()) { return false; } + const auto value_string = utility::us2s(value.as_string()); + bst::regex pattern(utility::us2s(nmos::fields::nc::pattern(constraints))); + if (!bst::regex_match(value_string, pattern)) { return false; } + } + + return true; + } + + // multiple levels of constraints validation + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + bool constraints_validation(const web::json::value& value, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const web::json::value& datatype_constraints) + { + // do level 2 runtime property constraints validation + if (!runtime_property_constraints.is_null()) { return constraints_validation(value, runtime_property_constraints); } + + // do level 1 property constraints validation + if (!property_constraints.is_null()) { return constraints_validation(value, property_constraints); } + + // do level 0 datatype constraints validation + if (!datatype_constraints.is_null()) { return constraints_validation(value, datatype_constraints); } + + // reaching here, no validation is required + return true; + } } diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index 72580351f..ec16841da 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -8,6 +8,15 @@ namespace nmos { struct control_protocol_resource; + namespace details + { + // get the runtime property constraints of a given property_id + web::json::value get_runtime_property_constraints(const nc_property_id& property_id, const web::json::value& runtime_property_constraints_list); + + // get the datatype property constraints of a given type_name + web::json::value get_datatype_constraints(const web::json::value& type_name, get_control_protocol_datatype_handler get_control_protocol_datatype); + } + // is the given class_id a NcBlock bool is_nc_block(const nc_class_id& class_id); @@ -47,6 +56,15 @@ namespace nmos // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id resources::const_iterator find_control_protocol_resource(resources& resources, type type, const id& id); + + // constraints validation + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring + bool constraints_validation(const web::json::value& value, const web::json::value& constraints); + + // multiple levels of constraints validation + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + bool constraints_validation(const web::json::value& value, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const web::json::value& data_constraints); } #endif diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index 0c293ac97..12bfa9bbf 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -322,9 +322,9 @@ namespace nmos const web::json::field_as_integer change_type{ U("changeType") }; // NcPropertyChangeType const web::json::field_as_integer sequence_item_index{ U("sequenceItemIndex") }; // NcId const web::json::field_as_value property_id{ U("propertyId") }; - const web::json::field_as_integer maximum{ U("maximum") }; - const web::json::field_as_integer minimum{ U("minimum") }; - const web::json::field_as_integer step{ U("step") }; + const web::json::field_as_value maximum{ U("maximum") }; + const web::json::field_as_value minimum{ U("minimum") }; + const web::json::field_as_value step{ U("step") }; const web::json::field_as_integer max_characters{ U("maxCharacters") }; const web::json::field_as_string pattern{ U("pattern") }; const web::json::field_as_value resource{ U("resource") }; diff --git a/Development/nmos/test/control_protocol_test.cpp b/Development/nmos/test/control_protocol_test.cpp index 91a115a42..1b8a4e5fe 100644 --- a/Development/nmos/test/control_protocol_test.cpp +++ b/Development/nmos/test/control_protocol_test.cpp @@ -700,3 +700,69 @@ BST_TEST_CASE(testFindProperty) BST_REQUIRE(property.is_null()); } } + +BST_TEST_CASE(testConstraints) +{ + using web::json::value_of; + using web::json::value; + + const nmos::nc_property_id property_string_id{ 100, 1 }; + const nmos::nc_property_id property_number_id{ 100, 2 }; + const nmos::nc_property_id unknown_property_id{ 100, 3 }; + + const auto runtime_property_string_constraints = nmos::details::make_nc_property_constraints_string(property_string_id, 10, U("^[0-9]+$")); + const auto runtime_property_number_constraints = nmos::details::make_nc_property_constraints_number(property_number_id, 10, 1000, 1); + + const auto runtime_property_constraints = value_of({ + { runtime_property_string_constraints }, + { runtime_property_number_constraints } + }); + + const auto property_string_constraints = nmos::details::make_nc_parameter_constraints_string(5, U("^[a-z]+$")); + const auto property_number_constraints = nmos::details::make_nc_parameter_constraints_number(50, 500, 5); + + const auto datatype_string_constraints = nmos::details::make_nc_parameter_constraints_string(2, U("^[0-9a-z]+$")); + const auto datatype_number_constraints = nmos::details::make_nc_parameter_constraints_number(100, 250, 10); + + // test get_runtime_property_constraints + BST_REQUIRE_EQUAL(nmos::details::get_runtime_property_constraints(property_string_id, runtime_property_constraints), runtime_property_string_constraints); + BST_REQUIRE_EQUAL(nmos::details::get_runtime_property_constraints(property_number_id, runtime_property_constraints), runtime_property_number_constraints); + BST_REQUIRE_EQUAL(nmos::details::get_runtime_property_constraints(unknown_property_id, runtime_property_constraints), value::null()); + + // string property constraints validation + BST_REQUIRE(nmos::constraints_validation(value::string(U("1234567890")), runtime_property_string_constraints, property_string_constraints, datatype_string_constraints)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("12345678901")), runtime_property_string_constraints, property_string_constraints, datatype_string_constraints), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("123456789A")), runtime_property_string_constraints, property_string_constraints, datatype_string_constraints), false); + BST_REQUIRE(nmos::constraints_validation(value::string(U("12345678901")), value::null(), value::null(), value::null())); + BST_REQUIRE(nmos::constraints_validation(value::string(U("123456789A")), value::null(), value::null(), value::null())); + + BST_REQUIRE(nmos::constraints_validation(value::string(U("abcde")), value::null(), property_string_constraints, datatype_string_constraints)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("abcdef")), value::null(), property_string_constraints, datatype_string_constraints), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("abcd1")), value::null(), property_string_constraints, datatype_string_constraints), false); + + BST_REQUIRE(nmos::constraints_validation(value::string(U("1a")), value::null(), value::null(), datatype_string_constraints)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("1a2")), value::null(), value::null(), datatype_string_constraints), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("1*")), value::null(), value::null(), datatype_string_constraints), false); + + // number property constraints validation + BST_REQUIRE(nmos::constraints_validation(10, runtime_property_number_constraints, property_number_constraints, datatype_number_constraints)); + BST_REQUIRE(nmos::constraints_validation(1000, runtime_property_number_constraints, property_number_constraints, datatype_number_constraints)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(9, runtime_property_number_constraints, property_number_constraints, datatype_number_constraints), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(1001, runtime_property_number_constraints, property_number_constraints, datatype_number_constraints), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(0.5, runtime_property_number_constraints, property_number_constraints, datatype_number_constraints), false); + BST_REQUIRE(nmos::constraints_validation(9, value::null(), value::null(), value::null())); + BST_REQUIRE(nmos::constraints_validation(1001, value::null(), value::null(), value::null())); + BST_REQUIRE(nmos::constraints_validation(0.5, value::null(), value::null(), value::null())); + + BST_REQUIRE(nmos::constraints_validation(50, value::null(), property_number_constraints, datatype_number_constraints)); + BST_REQUIRE(nmos::constraints_validation(500, value::null(), property_number_constraints, datatype_number_constraints)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(45, value::null(), property_number_constraints, datatype_number_constraints), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(505, value::null(), property_number_constraints, datatype_number_constraints), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(499, value::null(), property_number_constraints, datatype_number_constraints), false); + + BST_REQUIRE(nmos::constraints_validation(100, value::null(), value::null(), datatype_number_constraints)); + BST_REQUIRE(nmos::constraints_validation(250, value::null(), value::null(), datatype_number_constraints)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(90, value::null(), value::null(), datatype_number_constraints), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(260, value::null(), value::null(), datatype_number_constraints), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(99, value::null(), value::null(), datatype_number_constraints), false); +} From 677f66e7ce0b75b51886e1cc3feb5c04fc0769d4 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Mon, 30 Oct 2023 19:00:49 +0000 Subject: [PATCH 064/250] Update comments --- Development/nmos-cpp-node/node_implementation.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index e1c60fc3b..ed4a4f70c 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -921,7 +921,10 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr if (!insert_resource_after(delay_millis, model.channelmapping_resources, std::move(channelmapping_output), gate)) throw node_implementation_init_exception(); } - // example of using IS-12 control protocol + // examples of using IS-12 control protocol + // they are based on the NC-DEVICE-MOCK + // See https://specs.amwa.tv/nmos-device-control-mock/#about-nc-device-mock + // See https://github.com/AMWA-TV/nmos-device-control-mock/blob/main/code/src/NCModel/Features.ts if (0 <= nmos::fields::control_protocol_ws_port(model.settings)) { // example to create a non-standard Gain control class @@ -1102,7 +1105,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr { using web::json::value_of; - return web::json::value_of({ + return value_of({ { enum_property, enum_property_ }, { string_property, string_property_ }, { number_property, number_property_ }, From ad85dd0c13417d865736ce99ddd5bc0792108be0 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Mon, 30 Oct 2023 19:01:12 +0000 Subject: [PATCH 065/250] typo --- Development/nmos/control_protocol_state.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 80ff4254c..10e8eac49 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -160,7 +160,7 @@ namespace nmos // setup the core datatypes datatypes = { - // Dataype models + // Datatype models // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/#datatype-models-for-branch-v10-dev { U("NcClassId"), {make_nc_class_id_datatype()} }, { U("NcOid"), {make_nc_oid_datatype()} }, From 613beaf9a257fc83b6675cc6a7fd15726bbfcf41 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 31 Oct 2023 10:32:21 +0000 Subject: [PATCH 066/250] Add primitive types --- .../nmos/control_protocol_resource.cpp | 80 +++++++++++++++++ Development/nmos/control_protocol_resource.h | 20 +++++ Development/nmos/control_protocol_state.cpp | 89 +++++++++++++++++-- 3 files changed, 180 insertions(+), 9 deletions(-) diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 7a80aae17..61ab4b3ec 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -1304,6 +1304,86 @@ namespace nmos return details::make_nc_class_descriptor(U("NcReceiverMonitorProtected class descriptor"), nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), make_nc_receiver_monitor_protected_properties(), make_nc_receiver_monitor_protected_methods(), make_nc_receiver_monitor_protected_events()); } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_boolean_datatype() + { + using web::json::value; + + return details::make_nc_datatype_descriptor_primitive(U("Boolean primitive type"), U("NcBoolean"), value::null()); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_int16_datatype() + { + using web::json::value; + + return details::make_nc_datatype_descriptor_primitive(U("short"), U("NcInt16"), value::null()); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_int32_datatype() + { + using web::json::value; + + return details::make_nc_datatype_descriptor_primitive(U("long"), U("NcInt32"), value::null()); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_int64_datatype() + { + using web::json::value; + + return details::make_nc_datatype_descriptor_primitive(U("longlong"), U("NcInt64"), value::null()); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_uint16_datatype() + { + using web::json::value; + + return details::make_nc_datatype_descriptor_primitive(U("unsignedshort"), U("NcUint16"), value::null()); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_uint32_datatype() + { + using web::json::value; + + return details::make_nc_datatype_descriptor_primitive(U("unsignedlong"), U("NcUint32"), value::null()); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_uint64_datatype() + { + using web::json::value; + + return details::make_nc_datatype_descriptor_primitive(U("unsignedlonglong"), U("NcUint64"), value::null()); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_float32_datatype() + { + using web::json::value; + + return details::make_nc_datatype_descriptor_primitive(U("unrestrictedfloat"), U("NcFloat32"), value::null()); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_float64_datatype() + { + using web::json::value; + + return details::make_nc_datatype_descriptor_primitive(U("unrestricteddouble"), U("NcFloat64"), value::null()); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_string_datatype() + { + using web::json::value; + + return details::make_nc_datatype_descriptor_primitive(U("UTF-8 string"), U("NcString"), value::null()); + } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcBlockMemberDescriptor.html web::json::value make_nc_block_member_descriptor_datatype() { diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 1ff7e8e85..700f8e194 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -270,6 +270,26 @@ namespace nmos // Datatype models // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/#datatype-models-for-branch-v10-dev // + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_boolean_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_int16_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_int32_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_int64_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_uint16_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_uint32_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_uint64_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_float32_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_float64_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_string_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcBlockMemberDescriptor.html web::json::value make_nc_block_member_descriptor_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassDescriptor.html diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 10e8eac49..5ab2ccc32 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -117,9 +117,15 @@ namespace nmos { // Control class models // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/#control-class-models-for-branch-v10-dev - { nc_object_class_id, make_control_class(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), to_vector(make_nc_object_properties()), + + // NcObject + { nc_object_class_id, make_control_class(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), + // NcObject properties + to_vector(make_nc_object_properties()), + // NcObject methods to_methods_vector(make_nc_object_methods(), { + // link NcObject method_ids with method functions { nc_object_get_method_id, nmos::details::get }, { nc_object_set_method_id, nmos::details::set }, { nc_object_get_sequence_item_method_id, nmos::details::get_sequence_item }, @@ -128,33 +134,88 @@ namespace nmos { nc_object_remove_sequence_item_method_id, nmos::details::remove_sequence_item }, { nc_object_get_sequence_length_method_id, nmos::details::get_sequence_length } }), + // NcObject events to_vector(make_nc_object_events())) }, - { nc_block_class_id, make_control_class(U("NcBlock class descriptor"), nc_block_class_id, U("NcBlock"), to_vector(make_nc_block_properties()), + // NcBlock + { nc_block_class_id, make_control_class(U("NcBlock class descriptor"), nc_block_class_id, U("NcBlock"), + // NcBlock properties + to_vector(make_nc_block_properties()), + // NcBlock methods to_methods_vector(make_nc_block_methods(), { + // link NcBlock method_ids with method functions { nc_block_get_member_descriptors_method_id, nmos::details::get_member_descriptors }, { nc_block_find_members_by_path_method_id, nmos::details::find_members_by_path }, { nc_block_find_members_by_role_method_id, nmos::details::find_members_by_role }, { nc_block_find_members_by_class_id_method_id, nmos::details::find_members_by_class_id } }), + // NcBlock events to_vector(make_nc_block_events())) }, - { nc_worker_class_id, make_control_class(U("NcWorker class descriptor"), nc_worker_class_id, U("NcWorker"), to_vector(make_nc_worker_properties()), to_methods_vector(make_nc_worker_methods(), {}), to_vector(make_nc_worker_events())) }, - { nc_manager_class_id, make_control_class(U("NcManager class descriptor"), nc_manager_class_id, U("NcManager"),to_vector(make_nc_manager_properties()), to_methods_vector(make_nc_manager_methods(), {}), to_vector(make_nc_manager_events())) }, - { nc_device_manager_class_id, make_control_class(U("NcDeviceManager class descriptor"), nc_device_manager_class_id, U("NcDeviceManager"), U("DeviceManager"), to_vector(make_nc_device_manager_properties()), to_methods_vector(make_nc_device_manager_methods(), {}), to_vector(make_nc_device_manager_events())) }, - { nc_class_manager_class_id, make_control_class(U("NcClassManager class descriptor"), nc_class_manager_class_id, U("NcClassManager"), U("ClassManager"), to_vector(make_nc_class_manager_properties()), + // NcWorker + { nc_worker_class_id, make_control_class(U("NcWorker class descriptor"), nc_worker_class_id, U("NcWorker"), + // NcWorker properties + to_vector(make_nc_worker_properties()), + // NcWorker methods + to_methods_vector(make_nc_worker_methods(), {}), + // NcWorker events + to_vector(make_nc_worker_events())) }, + // NcManager + { nc_manager_class_id, make_control_class(U("NcManager class descriptor"), nc_manager_class_id, U("NcManager"), + // NcManager properties + to_vector(make_nc_manager_properties()), + // NcManager methods + to_methods_vector(make_nc_manager_methods(), {}), + // NcManager events + to_vector(make_nc_manager_events())) }, + // NcDeviceManager + { nc_device_manager_class_id, make_control_class(U("NcDeviceManager class descriptor"), nc_device_manager_class_id, U("NcDeviceManager"), U("DeviceManager"), + // NcDeviceManager properties + to_vector(make_nc_device_manager_properties()), + // NcDeviceManager methods + to_methods_vector(make_nc_device_manager_methods(), {}), + // NcDeviceManager events + to_vector(make_nc_device_manager_events())) }, + // NcClassManager + { nc_class_manager_class_id, make_control_class(U("NcClassManager class descriptor"), nc_class_manager_class_id, U("NcClassManager"), U("ClassManager"), + // NcClassManager properties + to_vector(make_nc_class_manager_properties()), + // NcClassManager methods to_methods_vector(make_nc_class_manager_methods(), { + // link NcClassManager method_ids with method functions { nc_class_manager_get_control_class_method_id, nmos::details::get_control_class }, { nc_class_manager_get_datatype_method_id, nmos::details::get_datatype } }), + // NcClassManager events to_vector(make_nc_class_manager_events())) }, // identification beacon model // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon - { nc_ident_beacon_class_id, make_control_class(U("NcIdentBeacon class descriptor"), nc_ident_beacon_class_id, U("NcIdentBeacon"), to_vector(make_nc_ident_beacon_properties()), to_methods_vector(make_nc_ident_beacon_methods(), {}), to_vector(make_nc_ident_beacon_events())) }, + // NcIdentBeacon + { nc_ident_beacon_class_id, make_control_class(U("NcIdentBeacon class descriptor"), nc_ident_beacon_class_id, U("NcIdentBeacon"), + // NcIdentBeacon properties + to_vector(make_nc_ident_beacon_properties()), + // NcIdentBeacon methods + to_methods_vector(make_nc_ident_beacon_methods(), {}), + // NcIdentBeacon events + to_vector(make_nc_ident_beacon_events())) }, // Monitoring // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor - { nc_receiver_monitor_class_id, make_control_class(U("NcReceiverMonitor class descriptor"), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), to_vector(make_nc_receiver_monitor_properties()), to_methods_vector(make_nc_receiver_monitor_methods(), {}), to_vector(make_nc_receiver_monitor_events())) }, - { nc_receiver_monitor_protected_class_id, make_control_class(U("NcReceiverMonitorProtected class descriptor"), nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), to_vector(make_nc_receiver_monitor_protected_properties()), to_methods_vector(make_nc_receiver_monitor_protected_methods(), {}), to_vector(make_nc_receiver_monitor_protected_events())) } + // NcReceiverMonitor + { nc_receiver_monitor_class_id, make_control_class(U("NcReceiverMonitor class descriptor"), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), + // NcReceiverMonitor properties + to_vector(make_nc_receiver_monitor_properties()), + // NcReceiverMonitor methods + to_methods_vector(make_nc_receiver_monitor_methods(), {}), + // NcReceiverMonitor events + to_vector(make_nc_receiver_monitor_events())) }, + // NcReceiverMonitorProtected + { nc_receiver_monitor_protected_class_id, make_control_class(U("NcReceiverMonitorProtected class descriptor"), nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), + // NcReceiverMonitorProtected properties + to_vector(make_nc_receiver_monitor_protected_properties()), + // NcReceiverMonitorProtected methods + to_methods_vector(make_nc_receiver_monitor_protected_methods(), {}), + // NcReceiverMonitorProtected events + to_vector(make_nc_receiver_monitor_protected_events())) } }; // setup the core datatypes @@ -162,6 +223,16 @@ namespace nmos { // Datatype models // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/#datatype-models-for-branch-v10-dev + { U("NcBoolean"), {make_nc_boolean_datatype()} }, + { U("NcInt16"), {make_nc_int16_datatype()} }, + { U("NcInt32"), {make_nc_int32_datatype()} }, + { U("NcInt64"), {make_nc_int64_datatype()} }, + { U("NcUint16"), {make_nc_uint16_datatype()} }, + { U("NcUint32"), {make_nc_uint32_datatype()} }, + { U("NcUint64"), {make_nc_uint64_datatype()} }, + { U("NcFloat32"), {make_nc_float32_datatype()} }, + { U("NcFloat64"), {make_nc_float64_datatype()} }, + { U("NcString"), {make_nc_string_datatype()} }, { U("NcClassId"), {make_nc_class_id_datatype()} }, { U("NcOid"), {make_nc_oid_datatype()} }, { U("NcTouchpoint"), {make_nc_touchpoint_datatype()} }, From cbedf53da8c6df1ca5d492c951cb2bc8811280ca Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 3 Nov 2023 11:28:41 +0000 Subject: [PATCH 067/250] Add a simple temperature sensor example, which pumps out new temperature value in a time interval --- .../nmos-cpp-node/node_implementation.cpp | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index ed4a4f70c..fa4293c02 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -976,7 +976,6 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr Beta = 2, Gamma = 3 }; - { // Example control class properties std::vector example_control_properties = { @@ -1168,6 +1167,32 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr return nmos::control_protocol_resource{ nmos::is12_versions::v1_0, nmos::types::nc_worker, std::move(data), true }; }; + // example to create a non-standard Temperature Sensor control class + const auto temperature_sensor_control_class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 3 }); + const web::json::field_as_number temperature{ U("temperature") }; + const web::json::field_as_string unit{ U("uint") }; + { + // Temperature Sensor control class properties + std::vector temperature_sensor_properties = { + nmos::experimental::make_control_class_property(U("Temperature"), { 3, 1 }, temperature, U("NcFloat32"), true), + nmos::experimental::make_control_class_property(U("Unit"), { 3, 2 }, unit, U("NcString"), true) + }; + + // create Temperature Sensor control class + auto temperature_sensor_control_class = nmos::experimental::make_control_class(U("Temperature Sensor control class descriptor"), temperature_sensor_control_class_id, U("TemperatureSensor"), temperature_sensor_properties); + + // insert Temperature Sensor control class to global state, which will be used by the control_protocol_ws_message_handler to process incoming ws message + control_protocol_state.insert(temperature_sensor_control_class); + } + // helper function to create Temperature Sensor control + auto make_temperature_sensor = [&temperature, &unit, temperature_sensor_control_class_id](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), float temperature_ = 0.0, const utility::string_t& unit_ = U("Celsius")) + { + auto data = nmos::details::make_nc_worker(temperature_sensor_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); + data[temperature] = value::number(temperature_); + data[unit] = value::string(unit_); + + return nmos::control_protocol_resource{ nmos::is12_versions::v1_0, nmos::types::nc_worker, std::move(data), true }; + }; // example root block auto root_block = nmos::make_root_block(); @@ -1244,6 +1269,11 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr } } + // example temperature-sensor + const auto temperature_sensor = make_temperature_sensor(++oid, nmos::root_block_oid, U("temperature-sensor"), U("Temperature Sensor"), U("Temperature Sensor block")); + + // add temperature-sensor to root-block + nmos::push_back(root_block, temperature_sensor); // add example-control to root-block nmos::push_back(root_block, example_control); // add stereo-gain to root-block @@ -1319,6 +1349,33 @@ void node_implementation_run(nmos::node_model& model, slog::base_gate& gate) } } + // update temperature sensor + { + const auto temperature_sensor_control_class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 3 }); + const web::json::field_as_number temperature{ U("temperature") }; + + auto& resources = model.control_protocol_resources; + + auto found = nmos::find_resource_if(resources, nmos::types::nc_worker, [&temperature_sensor_control_class_id](const nmos::resource& resource) + { + return temperature_sensor_control_class_id == nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + }); + + if (resources.end() != found) + { + const auto propertry_changed_event = nmos::make_propertry_changed_event(nmos::fields::nc::oid(found->data), + { + { {3, 1}, nmos::nc_property_change_type::type::value_changed, web::json::value(temp.scaled_value()) } + }); + + nmos::modify_control_protocol_resource(model.control_protocol_resources, found->id, [&](nmos::resource& resource) + { + resource.data[temperature] = temp.scaled_value(); + + }, propertry_changed_event); + } + } + slog::log(gate, SLOG_FLF) << "Temperature updated: " << temp.scaled_value() << " (" << impl::temperature_Celsius.name << ")"; model.notify(); From d21ca754cbe1df9774e6d13685b541b8ccf20698 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 7 Nov 2023 18:36:23 +0000 Subject: [PATCH 068/250] Fix non-standard class's method handing --- Development/nmos-cpp-node/main.cpp | 2 +- .../nmos-cpp-node/node_implementation.cpp | 12 +++++-- .../nmos/control_protocol_handlers.cpp | 26 +++++++++++----- Development/nmos/control_protocol_handlers.h | 12 ++++--- Development/nmos/control_protocol_state.cpp | 12 +++---- Development/nmos/control_protocol_state.h | 4 +-- Development/nmos/control_protocol_ws_api.cpp | 31 ++----------------- Development/nmos/control_protocol_ws_api.h | 6 ++-- Development/nmos/node_server.cpp | 2 +- Development/nmos/node_server.h | 8 ++--- 10 files changed, 55 insertions(+), 60 deletions(-) diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index ae9d4380f..dbd2a8f0c 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -114,7 +114,7 @@ int main(int argc, char* argv[]) { node_implementation.on_get_control_class(nmos::make_get_control_protocol_class_handler(control_protocol_state)); node_implementation.on_get_control_datatype(nmos::make_get_control_protocol_datatype_handler(control_protocol_state)); - node_implementation.on_get_control_protocol_methods(nmos::make_get_control_protocol_methods_handler(control_protocol_state)); + node_implementation.on_get_control_protocol_method(nmos::make_get_control_protocol_method_handler(control_protocol_state)); } // Set up the node server diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index fa4293c02..67360c8c8 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1002,12 +1002,16 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr auto example_method_with_no_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + slog::log(gate, SLOG_FLF) << "Executing the example method with no arguments"; return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::ok }); }; - auto example_method_with_simple_args = [&](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + auto example_method_with_simple_args = [enum_arg, string_arg, number_arg, boolean_arg, make_string_example_argument_constraints, make_number_example_argument_constraints](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + slog::log(gate, SLOG_FLF) << "Executing the example method with simple arguments:" << " enum_arg: " << enum_arg(arguments).to_int32() @@ -1028,8 +1032,10 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::ok }); }; - auto example_method_with_object_args = [&](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + auto example_method_with_object_args = [obj_arg](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + slog::log(gate, SLOG_FLF) << "Executing the example method with object argument:" << " obj_arg: " << obj_arg(arguments).serialize(); @@ -1037,7 +1043,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::ok }); }; // Example control class methods - std::vector> example_control_methods = + std::vector> example_control_methods = { { nmos::experimental::make_control_class_method(U("Example method with no arguments"), { 3, 1 }, U("MethodNoArgs"), U("NcMethodResult"), {}, false), example_method_with_no_args }, { nmos::experimental::make_control_class_method(U("Example method with simple arguments"), { 3, 2 }, U("MethodSimpleArgs"), U("NcMethodResult"), diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index f51b85cf6..e650496aa 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -38,21 +38,31 @@ namespace nmos }; } - get_control_protocol_methods_handler make_get_control_protocol_methods_handler(experimental::control_protocol_state& control_protocol_state) + get_control_protocol_method_handler make_get_control_protocol_method_handler(experimental::control_protocol_state& control_protocol_state) { - return [&]() + return [&](const nc_class_id& class_id_, const nc_method_id& method_id) { - std::map methods; + auto class_id = class_id_; - auto lock = control_protocol_state.read_lock(); + auto get_control_protocol_class = make_get_control_protocol_class_handler(control_protocol_state); - auto& control_classes = control_protocol_state.control_classes; + auto lock = control_protocol_state.read_lock(); - for (const auto& control_class : control_classes) + while (!class_id.empty()) { - methods[control_class.first] = control_class.second.method_handlers; + const auto& control_class = get_control_protocol_class(class_id); + auto& methods = control_class.method_handlers; + + auto method_found = methods.find(method_id); + if (methods.end() != method_found) + { + return method_found->second; + } + + class_id.pop_back(); } - return methods; + + return experimental::method_handler(nullptr); }; } diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index bab1904f4..a21277612 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -35,23 +35,27 @@ namespace nmos namespace experimental { // method handler defnition - typedef std::function method; + typedef std::function method_handler; // methods defnition - typedef std::map methods; // method_id vs method handler + typedef std::map methods; // method_id vs method handler } // callback to retrieve all the method handlers // this callback should not throw exceptions typedef std::function()> get_control_protocol_methods_handler; + // callback to retrieve a specific method handler + // this callback should not throw exceptions + typedef std::function get_control_protocol_method_handler; + // construct callback to retrieve a specific control protocol class get_control_protocol_class_handler make_get_control_protocol_class_handler(experimental::control_protocol_state& control_protocol_state); // construct callback to retrieve a specific datatype get_control_protocol_datatype_handler make_get_control_protocol_datatype_handler(experimental::control_protocol_state& control_protocol_state); - // construct callback to retrieve all method handlers - get_control_protocol_methods_handler make_get_control_protocol_methods_handler(experimental::control_protocol_state& control_protocol_state); + // construct callback to retrieve a specific method handler + get_control_protocol_method_handler make_get_control_protocol_method_handler(experimental::control_protocol_state& control_protocol_state); // a control_protocol_connection_activation_handler is a notification that the active parameters for the specified (IS-05) sender/connection_sender or receiver/connection_receiver have changed // this callback should not throw exceptions diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 5ab2ccc32..60661b966 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -11,10 +11,10 @@ namespace nmos { // create control class // where - // properties: vector of NcPropertyDescriptor which can be constructed using make_control_class_property - // methods: vector of NcMethodDescriptor which can be constructed using make_nc_method_descriptor and the assoicated method handler + // properties: vector of NcPropertyDescriptor can be constructed using make_control_class_property + // methods: vector of NcMethodDescriptor can be constructed using make_nc_method_descriptor and the assoicated method handler // events: vector of NcEventDescriptor can be constructed using make_nc_event_descriptor - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const std::vector& properties_, const std::vector>& methods_, const std::vector& events_) + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const std::vector& properties_, const std::vector>& methods_, const std::vector& events_) { using web::json::value; @@ -38,7 +38,7 @@ namespace nmos // properties: vector of NcPropertyDescriptor which can be constructed using make_control_class_property // methods: vector of NcMethodDescriptor which can be constructed using make_nc_method_descriptor and the assoicated method handler // events: vector of NcEventDescriptor can be constructed using make_nc_event_descriptor - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const std::vector& properties, const std::vector>& methods, const std::vector& events) + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const std::vector& properties, const std::vector>& methods, const std::vector& events) { using web::json::value; @@ -49,7 +49,7 @@ namespace nmos // properties: vector of NcPropertyDescriptor which can be constructed using make_control_class_property // methods: vector of NcMethodDescriptor which can be constructed using make_nc_method_descriptor and the assoicated method handler // events: vector of NcEventDescriptor can be constructed using make_nc_event_descriptor - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const std::vector& properties, const std::vector>& methods, const std::vector& events) + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const std::vector& properties, const std::vector>& methods, const std::vector& events) { using web::json::value; @@ -100,7 +100,7 @@ namespace nmos auto to_methods_vector = [](const web::json::value& method_data_array, const nmos::experimental::methods& method_handlers) { - std::vector> methods; + std::vector> methods; if (!method_data_array.is_null()) { diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index b95c26e48..54431fca8 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -92,9 +92,9 @@ namespace nmos bool is_read_only = false, bool is_nullable = false, bool is_sequence = false, bool is_deprecated = false, const web::json::value& constraints = web::json::value::null()); // create control class with fixed role - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const std::vector& properties = {}, const std::vector>& methods = {}, const std::vector& events = {}); + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const std::vector& properties = {}, const std::vector>& methods = {}, const std::vector& events = {}); // create control class with no fixed role - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const std::vector& properties = {}, const std::vector>& methods = {}, const std::vector& events = {}); + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const std::vector& properties = {}, const std::vector>& methods = {}, const std::vector& events = {}); } } diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 26948a8c9..beb54823d 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -45,29 +45,6 @@ namespace nmos { controlprotocol_validator().validate(request_data, experimental::make_controlprotocolapi_subscription_message_schema_uri(version)); } - - nmos::experimental::method find_method(const nc_method_id& method_id, const nc_class_id& class_id_, const std::map& methods) - { - auto class_id = class_id_; - - while (!class_id.empty()) - { - auto class_id_methods_found = methods.find(class_id); - - if (methods.end() != class_id_methods_found) - { - auto& method_id_methods = class_id_methods_found->second; - auto method_found = method_id_methods.find(method_id); - if (method_id_methods.end() != method_found) - { - return method_found->second; - } - } - class_id.pop_back(); - } - - return nullptr; - } } // IS-12 Control Protocol WebSocket API @@ -206,14 +183,12 @@ namespace nmos }; } - web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_methods_handler get_control_protocol_methods, slog::base_gate& gate_) + web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_method_handler get_control_protocol_method, slog::base_gate& gate_) { using web::json::value; using web::json::value_of; - auto methods = get_control_protocol_methods(); - - return [&model, &websockets, get_control_protocol_class, get_control_protocol_datatype, methods, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id, const web::websockets::websocket_incoming_message& msg_) + return [&model, &websockets, get_control_protocol_class, get_control_protocol_datatype, get_control_protocol_method, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id, const web::websockets::websocket_incoming_message& msg_) { nmos::ws_api_gate gate(gate_, connection_uri); @@ -276,7 +251,7 @@ namespace nmos const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); // find the relevent method handler to execute - auto method = details::find_method(method_id, class_id, methods); + auto method = get_control_protocol_method(class_id, method_id); if (method) { // execute the relevant method handler, then accumulating up their response to reponses diff --git a/Development/nmos/control_protocol_ws_api.h b/Development/nmos/control_protocol_ws_api.h index 7fb79a520..bbb686272 100644 --- a/Development/nmos/control_protocol_ws_api.h +++ b/Development/nmos/control_protocol_ws_api.h @@ -16,15 +16,15 @@ namespace nmos web::websockets::experimental::listener::validate_handler make_control_protocol_ws_validate_handler(nmos::node_model& model, slog::base_gate& gate); web::websockets::experimental::listener::open_handler make_control_protocol_ws_open_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate); web::websockets::experimental::listener::close_handler make_control_protocol_ws_close_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate); - web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_methods_handler get_control_protocol_methods, slog::base_gate& gate); + web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_method_handler get_control_protocol_method, slog::base_gate& gate); - inline web::websockets::experimental::listener::websocket_listener_handlers make_control_protocol_ws_api(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_methods_handler get_control_protocol_methods, slog::base_gate& gate) + inline web::websockets::experimental::listener::websocket_listener_handlers make_control_protocol_ws_api(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_method_handler get_control_protocol_method, slog::base_gate& gate) { return{ nmos::make_control_protocol_ws_validate_handler(model, gate), nmos::make_control_protocol_ws_open_handler(model, websockets, gate), nmos::make_control_protocol_ws_close_handler(model, websockets, gate), - nmos::make_control_protocol_ws_message_handler(model, websockets, get_control_protocol_class, get_control_protocol_datatype, get_control_protocol_methods, gate) + nmos::make_control_protocol_ws_message_handler(model, websockets, get_control_protocol_class, get_control_protocol_datatype, get_control_protocol_method, gate) }; } diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index ccc9d1e3f..babb2c045 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -75,7 +75,7 @@ namespace nmos { if (control_protocol_ws_port == events_ws_port) throw std::runtime_error("Same port used for events and control protocol websockets are not supported"); auto& control_protocol_ws_api = node_server.ws_handlers[{ {}, control_protocol_ws_port }]; - control_protocol_ws_api.first = nmos::make_control_protocol_ws_api(node_model, control_protocol_ws_api.second, node_implementation.get_control_protocol_class, node_implementation.get_control_protocol_datatype, node_implementation.get_control_protocol_methods, gate); + control_protocol_ws_api.first = nmos::make_control_protocol_ws_api(node_model, control_protocol_ws_api.second, node_implementation.get_control_protocol_class, node_implementation.get_control_protocol_datatype, node_implementation.get_control_protocol_method, gate); } // Set up the listeners for each HTTP API port diff --git a/Development/nmos/node_server.h b/Development/nmos/node_server.h index 95fdca058..a58b17e05 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -25,7 +25,7 @@ namespace nmos // underlying implementation into the server instance for the NMOS Node struct node_implementation { - node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_methods_handler get_control_protocol_methods) + node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_method_handler get_control_protocol_method) : load_server_certificates(std::move(load_server_certificates)) , load_dh_param(std::move(load_dh_param)) , load_ca_certificates(std::move(load_ca_certificates)) @@ -39,7 +39,7 @@ namespace nmos , get_ocsp_response(std::move(get_ocsp_response)) , get_control_protocol_class(std::move(get_control_protocol_class)) , get_control_protocol_datatype(std::move(get_control_protocol_datatype)) - , get_control_protocol_methods(std::move(get_control_protocol_methods)) + , get_control_protocol_method(std::move(get_control_protocol_method)) {} // use the default constructor and chaining member functions for fluent initialization @@ -63,7 +63,7 @@ namespace nmos node_implementation& on_get_ocsp_response(nmos::ocsp_response_handler get_ocsp_response) { this->get_ocsp_response = std::move(get_ocsp_response); return *this; } node_implementation& on_get_control_class(nmos::get_control_protocol_class_handler get_control_protocol_class) { this->get_control_protocol_class = std::move(get_control_protocol_class); return *this; } node_implementation& on_get_control_datatype(nmos::get_control_protocol_datatype_handler get_control_protocol_datatype) { this->get_control_protocol_datatype = std::move(get_control_protocol_datatype); return *this; } - node_implementation& on_get_control_protocol_methods(nmos::get_control_protocol_methods_handler get_control_protocol_methods) { this->get_control_protocol_methods = std::move(get_control_protocol_methods); return *this; } + node_implementation& on_get_control_protocol_method(nmos::get_control_protocol_method_handler get_control_protocol_method) { this->get_control_protocol_method = std::move(get_control_protocol_method); return *this; } // deprecated, use on_validate_connection_resource_patch node_implementation& on_validate_merged(nmos::details::connection_resource_patch_validator validate_merged) { return on_validate_connection_resource_patch(std::move(validate_merged)); } @@ -96,7 +96,7 @@ namespace nmos nmos::get_control_protocol_class_handler get_control_protocol_class; nmos::get_control_protocol_datatype_handler get_control_protocol_datatype; - nmos::get_control_protocol_methods_handler get_control_protocol_methods; + nmos::get_control_protocol_method_handler get_control_protocol_method; }; // Construct a server instance for an NMOS Node, implementing the IS-04 Node API, IS-05 Connection API, IS-07 Events API From f039f8f41a09a4377cafd91e40a86a1417ed56b2 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 8 Nov 2023 16:04:43 +0000 Subject: [PATCH 069/250] Remove un-used code and fix typo --- Development/nmos-cpp-node/node_implementation.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 67360c8c8..31ad30004 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -980,9 +980,9 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // Example control class properties std::vector example_control_properties = { nmos::experimental::make_control_class_property(U("Example enum property"), { 3, 1 }, enum_property, U("ExampleEnum")), - // create "Example string property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + // create "Example string property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html nmos::experimental::make_control_class_property(U("Example string property"), { 3, 2 }, string_property, U("NcString"), false, false, false, false, nmos::details::make_nc_parameter_constraints_string(10)), - // create "Example numeric property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + // create "Example numeric property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html nmos::experimental::make_control_class_property(U("Example numeric property"), { 3, 3 }, number_property, U("NcUint64"), false, false, false, false, nmos::details::make_nc_parameter_constraints_number(0, 1000, 1)), nmos::experimental::make_control_class_property(U("Example boolean property"), { 3, 4 }, boolean_property, U("NcBoolean")), nmos::experimental::make_control_class_property(U("Example object property"), { 3, 5 }, object_property, U("ExampleDataType")), @@ -1023,7 +1023,6 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr << boolean_arg(arguments); // example to do method arguments constraints validation - const auto string_example_argument_constraints = make_string_example_argument_constraints(); if (!nmos::constraints_validation(arguments.at(string_arg), make_string_example_argument_constraints()) || !nmos::constraints_validation(arguments.at(number_arg), make_number_example_argument_constraints())) { @@ -1120,7 +1119,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // helper function to create Example control auto make_example_control = [&](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const value& touchpoints = value::null(), const value& runtime_property_constraints = value::null(), // level 2: runtime constraints. See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use of make_nc_property_constraints_stringand make_nc_property_constraints_number to create runtime constraints + // use of make_nc_property_constraints_string and make_nc_property_constraints_number to create runtime constraints example_enum enum_property_ = example_enum::Undefined, const utility::string_t& string_property_ = U(""), uint64_t number_property_ = 0, From 98b4b5654056492fa9c47eb565c681d66cc625b1 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 8 Nov 2023 16:10:48 +0000 Subject: [PATCH 070/250] Test readonly on set_sequence_item and add_sequence_item --- Development/nmos/control_protocol_methods.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index de2dcba4b..04b891b69 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -143,6 +143,11 @@ namespace nmos const auto& property = find_property(property_id_, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); if (!property.is_null()) { + if (nmos::fields::nc::is_read_only(property)) + { + return make_control_protocol_message_response(handle, { nc_method_status::read_only }); + } + auto& data = resource->data.at(nmos::fields::nc::name(property)); if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) @@ -200,6 +205,11 @@ namespace nmos const auto& property = find_property(property_id_, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); if (!property.is_null()) { + if (nmos::fields::nc::is_read_only(property)) + { + return make_control_protocol_message_response(handle, { nc_method_status::read_only }); + } + if (!nmos::fields::nc::is_sequence(property)) { // property is not a sequence From b14afefb4c67b95908cf8cc3d4b9ffa78356ce28 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 14 Nov 2023 15:27:38 +0000 Subject: [PATCH 071/250] Enhance level 0 datatype constraints validation --- Development/nmos/control_protocol_methods.cpp | 6 +- Development/nmos/control_protocol_resource.h | 6 + Development/nmos/control_protocol_utils.cpp | 284 +++++-- Development/nmos/control_protocol_utils.h | 15 +- .../nmos/test/control_protocol_test.cpp | 741 +++++++++++++++++- 5 files changed, 934 insertions(+), 118 deletions(-) diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index 04b891b69..feba52442 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -64,7 +64,7 @@ namespace nmos // do constraints validation if (!val.is_null()) { - if (!constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), get_datatype_constraints(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype))) + if (!constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) { return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); } @@ -161,7 +161,7 @@ namespace nmos if (data.as_array().size() > (size_t)index) { // do constraints validation - if (!constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), get_datatype_constraints(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype))) + if (!constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) { return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); } @@ -223,7 +223,7 @@ namespace nmos const nc_id sequence_item_index = data.is_null() ? 0 : nc_id(data.as_array().size()); // do constraints validation - if (!constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), get_datatype_constraints(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype))) + if (!constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) { return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); } diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 700f8e194..193e9a800 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -130,6 +130,9 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptortypedef web::json::value make_nc_datatype_typedef(const utility::string_t& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraints + web::json::value make_nc_property_constraints(const nc_property_id& property_id, const web::json::value& default_value); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsnumber web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step); web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t minimum, uint64_t maximum, uint64_t step); @@ -140,6 +143,9 @@ namespace nmos web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters); web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const nc_regex& pattern); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraints + web::json::value make_nc_parameter_constraints(const web::json::value& default_value); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber web::json::value make_nc_parameter_constraints_number(uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step); web::json::value make_nc_parameter_constraints_number(uint64_t minimum, uint64_t maximum, uint64_t step); diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index e03c6697f..d1f2f933f 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -26,7 +26,7 @@ namespace nmos return control_class_id == class_id; } - // get the runtime property constraints of a given property_id + // get the runtime property constraints of a specific property_id web::json::value get_runtime_property_constraints(const nc_property_id& property_id, const web::json::value& runtime_property_constraints) { using web::json::value; @@ -48,20 +48,222 @@ namespace nmos return value::null(); } - // get the datatype property constraints of a given type_name - web::json::value get_datatype_constraints(const web::json::value& type_name, get_control_protocol_datatype_handler get_control_protocol_datatype) + // get the datatype descriptor of a specific type_name + web::json::value get_datatype_descriptor(const web::json::value& type_name, get_control_protocol_datatype_handler get_control_protocol_datatype) { using web::json::value; if (!type_name.is_null()) { - const auto& datatype = get_control_protocol_datatype(type_name.as_string()); - if (!datatype.descriptor.is_null()) // NcDatatypeDescriptor + return get_control_protocol_datatype(type_name.as_string()).descriptor; + } + return value::null(); + } + + // get the datatype property constraints of a specific type_name + web::json::value get_datatype_constraints(const web::json::value& type_name, get_control_protocol_datatype_handler get_control_protocol_datatype) + { + using web::json::value; + + // NcDatatypeDescriptor + const auto& datatype_descriptor = get_datatype_descriptor(type_name, get_control_protocol_datatype); + if (!datatype_descriptor.is_null()) + { + return nmos::fields::nc::constraints(datatype_descriptor); + } + return value::null(); + } + + // constraints validation + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring + bool constraints_validation(const web::json::value& value, const web::json::value& constraints) + { + // is numeric constraints + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber + if (constraints.has_field(nmos::fields::nc::step) && !nmos::fields::nc::step(constraints).is_null()) + { + if (!value.is_integer()) { return false; } + + const auto step = nmos::fields::nc::step(constraints).as_double(); + if (step <= 0) { return false; } + + const auto value_double = value.as_double(); + if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) + { + auto min = nmos::fields::nc::minimum(constraints).as_double(); + if (0 != std::fmod(value_double - min, step)) { return false; } + } + else if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) + { + auto max = nmos::fields::nc::maximum(constraints).as_double(); + if (0 != std::fmod(max - value_double, step)) { return false; } + } + else { - return nmos::fields::nc::constraints(datatype.descriptor); + if (0 != std::fmod(value_double, step)) { return false; } } } - return value::null(); + if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) + { + if (!value.is_integer() || value.as_double() < nmos::fields::nc::minimum(constraints).as_double()) { return false; } + } + if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) + { + if (!value.is_integer() || value.as_double() > nmos::fields::nc::maximum(constraints).as_double()) { return false; } + } + + // is string constraints + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring + if (constraints.has_field(nmos::fields::nc::max_characters) && !constraints.at(nmos::fields::nc::max_characters).is_null()) + { + const auto max_characters = nmos::fields::nc::max_characters(constraints); + if (!value.is_string() || value.as_string().length() > max_characters) { return false; } + } + if (constraints.has_field(nmos::fields::nc::pattern) && !constraints.at(nmos::fields::nc::pattern).is_null()) + { + if (!value.is_string()) { return false; } + const auto value_string = utility::us2s(value.as_string()); + bst::regex pattern(utility::us2s(nmos::fields::nc::pattern(constraints))); + if (!bst::regex_match(value_string, pattern)) { return false; } + } + + // reaching here, no validation is required + return true; + } + + // level 0 datatype constraints validation + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + bool datatype_constraints_validation(const web::json::value& data, const datatype_constraints_validation_parameters& params) + { + const auto& datatype_type = nmos::fields::nc::type(params.datatype_descriptor); + + auto is_int16 = [](int32_t value) + { + return value >= (std::numeric_limits::min)() + && value <= (std::numeric_limits::max)(); + }; + auto is_uint16 = [](uint32_t value) + { + return value >= (std::numeric_limits::min)() + && value <= (std::numeric_limits::max)(); + }; + auto is_float32 = [](double value) + { + return value >= (std::numeric_limits::min)() + && value <= (std::numeric_limits::max)(); + }; + + // do NcDatatypeDescriptorPrimitive constraints validation + if (nc_datatype_type::Primitive == datatype_type) + { + // hmm, for the primitive type, it should not have datatype constraints specified via the datatype_descriptor but just in case + const auto& datatype_constraints = nmos::fields::nc::constraints(params.datatype_descriptor); + if (!datatype_constraints.is_null()) { return constraints_validation(data, datatype_constraints); } + + // do primitive type constraints + const auto& name = nmos::fields::nc::name(params.datatype_descriptor); + if (U("NcBoolean") == name) { return data.is_boolean(); } + if (U("NcInt16") == name && data.is_number()) { return is_int16(data.as_number().to_int32()); } + if (U("NcInt32") == name && data.is_number()) { return data.as_number().is_int32(); } + if (U("NcInt64") == name && data.is_number()) { return data.as_number().is_int64(); } + if (U("NcUint16") == name && data.is_number()) { return is_uint16(data.as_number().to_uint32()); } + if (U("NcUint32") == name && data.is_number()) { return data.as_number().is_uint32(); } + if (U("NcUint64") == name && data.is_number()) { return data.as_number().is_uint64(); } + if (U("NcFloat32") == name && data.is_number()) { return is_float32(data.as_number().to_double()); } + if (U("NcFloat64") == name && data.is_number()) { return !data.as_number().is_integral(); } + if (U("NcString") == name) { return data.is_string(); } + + // invalid primitive type + return false; + } + + // do NcDatatypeDescriptorTypeDef constraints validation + if (nc_datatype_type::Typedef == datatype_type) + { + // do the datatype constraints specified via the datatype_descriptor if presented + const auto& datatype_constraints = nmos::fields::nc::constraints(params.datatype_descriptor); + if (!datatype_constraints.is_null()) { return constraints_validation(data, datatype_constraints); } + + // do parent typename constraints validation + const auto& type_name = params.datatype_descriptor.at(nmos::fields::nc::parent_type); // parent type_name + if (!datatype_constraints_validation(data, { details::get_datatype_descriptor(type_name, params.get_control_protocol_datatype), params.get_control_protocol_datatype })) { return false; } + } + + // do NcDatatypeDescriptorEnum constraints validation + if (nc_datatype_type::Enum == datatype_type) + { + const auto& items = nmos::fields::nc::items(params.datatype_descriptor); + return (items.end() != std::find_if(items.begin(), items.end(), [&](const web::json::value& nc_enum_item_descriptor) { return nmos::fields::nc::value(nc_enum_item_descriptor) == data; })); + } + + // do NcDatatypeDescriptorStruct constraints validation + if (nc_datatype_type::Struct == datatype_type) + { + const auto& fields = nmos::fields::nc::fields(params.datatype_descriptor); + // NcFieldDescriptor + for (const web::json::value& nc_field_descriptor : fields) + { + const auto& name = nmos::fields::nc::name(nc_field_descriptor); + // check is the specific element in value strurcture + if (!data.has_field(name)) { return false; } + + // check is the element is a nullable field + if (nmos::fields::nc::is_nullable(nc_field_descriptor) != data.is_null()) { return false; } + + // check is the element is a sequence field + if (nmos::fields::nc::is_sequence(nc_field_descriptor) != data.is_array()) { return false; } + + // check against field constraints if presented + const auto& constraints = nmos::fields::nc::constraints(nc_field_descriptor); + if (!constraints.is_null()) + { + auto value = data.at(name); + + if (value.is_array()) + { + for (const auto& val : value.as_array()) + { + // do field constraints validation + if (!constraints_validation(val, constraints)) { return false; } + } + } + else + { + // do field constraints validation + if (!constraints_validation(value, constraints)) { return false; } + } + } + else + { + // no field constraints, move to check the constraints of its typeName + const auto& type_name = nc_field_descriptor.at(nmos::fields::nc::type_name); + + if (!type_name.is_null()) + { + auto value = data.at(name); + + if (value.is_array()) + { + for (const auto& val : value.as_array()) + { + // do typename constraints validation + if (!datatype_constraints_validation(val, { details::get_datatype_descriptor(type_name, params.get_control_protocol_datatype), params.get_control_protocol_datatype })) { return false; } + } + } + else + { + // do typename constraints validation + if (!datatype_constraints_validation(value, { details::get_datatype_descriptor(type_name, params.get_control_protocol_datatype), params.get_control_protocol_datatype })) { return false; } + } + } + } + } + return true; + } + + // unsupport datatype_type, no validation is required + return true; } } @@ -338,77 +540,17 @@ namespace nmos }); } - // constraints validation - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring - bool constraints_validation(const web::json::value& value, const web::json::value& constraints) - { - // is numeric constraints - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber - if (constraints.has_field(nmos::fields::nc::step) && !nmos::fields::nc::step(constraints).is_null()) - { - if (!value.is_integer()) { return false; } - - const auto step = nmos::fields::nc::step(constraints).as_double(); - if (step <= 0) { return false; } - - const auto value_double = value.as_double(); - if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) - { - auto min = nmos::fields::nc::minimum(constraints).as_double(); - if (0 != std::fmod(value_double - min, step)) { return false; } - } - else if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) - { - auto max = nmos::fields::nc::maximum(constraints).as_double(); - if (0 != std::fmod(max - value_double, step)) { return false; } - } - else - { - if (0 != std::fmod(value_double, step)) { return false; } - } - } - if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) - { - if (!value.is_integer() || value.as_double() < nmos::fields::nc::minimum(constraints).as_double()) { return false; } - } - if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) - { - if (!value.is_integer() || value.as_double() > nmos::fields::nc::maximum(constraints).as_double()) { return false; } - } - - // is string constraints - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring - if (constraints.has_field(nmos::fields::nc::max_characters) && !constraints.at(nmos::fields::nc::max_characters).is_null()) - { - const auto max_characters = nmos::fields::nc::max_characters(constraints); - if (!value.is_string() || value.as_string().length() > max_characters) { return false; } - } - if (constraints.has_field(nmos::fields::nc::pattern) && !constraints.at(nmos::fields::nc::pattern).is_null()) - { - if (!value.is_string()) { return false; } - const auto value_string = utility::us2s(value.as_string()); - bst::regex pattern(utility::us2s(nmos::fields::nc::pattern(constraints))); - if (!bst::regex_match(value_string, pattern)) { return false; } - } - - return true; - } - // multiple levels of constraints validation // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - bool constraints_validation(const web::json::value& value, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const web::json::value& datatype_constraints) + bool constraints_validation(const web::json::value& value, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) { // do level 2 runtime property constraints validation - if (!runtime_property_constraints.is_null()) { return constraints_validation(value, runtime_property_constraints); } + if (!runtime_property_constraints.is_null()) { return details::constraints_validation(value, runtime_property_constraints); } // do level 1 property constraints validation - if (!property_constraints.is_null()) { return constraints_validation(value, property_constraints); } + if (!property_constraints.is_null()) { return details::constraints_validation(value, property_constraints); } // do level 0 datatype constraints validation - if (!datatype_constraints.is_null()) { return constraints_validation(value, datatype_constraints); } - - // reaching here, no validation is required - return true; + return details::datatype_constraints_validation(value, params); } } diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index ec16841da..1718e01cc 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -13,6 +13,9 @@ namespace nmos // get the runtime property constraints of a given property_id web::json::value get_runtime_property_constraints(const nc_property_id& property_id, const web::json::value& runtime_property_constraints_list); + // get the datatype descriptor of a specific type_name + web::json::value get_datatype_descriptor(const web::json::value& type_name, get_control_protocol_datatype_handler get_control_protocol_datatype); + // get the datatype property constraints of a given type_name web::json::value get_datatype_constraints(const web::json::value& type_name, get_control_protocol_datatype_handler get_control_protocol_datatype); } @@ -57,14 +60,14 @@ namespace nmos // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id resources::const_iterator find_control_protocol_resource(resources& resources, type type, const id& id); - // constraints validation - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring - bool constraints_validation(const web::json::value& value, const web::json::value& constraints); - + struct datatype_constraints_validation_parameters + { + web::json::value datatype_descriptor; + get_control_protocol_datatype_handler get_control_protocol_datatype; + }; // multiple levels of constraints validation // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - bool constraints_validation(const web::json::value& value, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const web::json::value& data_constraints); + bool constraints_validation(const web::json::value& value, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params); } #endif diff --git a/Development/nmos/test/control_protocol_test.cpp b/Development/nmos/test/control_protocol_test.cpp index 1b8a4e5fe..7ffd6d59f 100644 --- a/Development/nmos/test/control_protocol_test.cpp +++ b/Development/nmos/test/control_protocol_test.cpp @@ -707,62 +707,727 @@ BST_TEST_CASE(testConstraints) using web::json::value; const nmos::nc_property_id property_string_id{ 100, 1 }; - const nmos::nc_property_id property_number_id{ 100, 2 }; + const nmos::nc_property_id property_int32_id{ 100, 2 }; const nmos::nc_property_id unknown_property_id{ 100, 3 }; + // constraints + + // runtime constraints const auto runtime_property_string_constraints = nmos::details::make_nc_property_constraints_string(property_string_id, 10, U("^[0-9]+$")); - const auto runtime_property_number_constraints = nmos::details::make_nc_property_constraints_number(property_number_id, 10, 1000, 1); + const auto runtime_property_int32_constraints = nmos::details::make_nc_property_constraints_number(property_int32_id, 10, 1000, 1); const auto runtime_property_constraints = value_of({ { runtime_property_string_constraints }, - { runtime_property_number_constraints } + { runtime_property_int32_constraints } }); + // propertry constraints const auto property_string_constraints = nmos::details::make_nc_parameter_constraints_string(5, U("^[a-z]+$")); - const auto property_number_constraints = nmos::details::make_nc_parameter_constraints_number(50, 500, 5); + const auto property_int32_constraints = nmos::details::make_nc_parameter_constraints_number(50, 500, 5); + // datatype constraints const auto datatype_string_constraints = nmos::details::make_nc_parameter_constraints_string(2, U("^[0-9a-z]+$")); - const auto datatype_number_constraints = nmos::details::make_nc_parameter_constraints_number(100, 250, 10); + const auto datatype_int32_constraints = nmos::details::make_nc_parameter_constraints_number(100, 250, 10); + + // datatypes + const auto no_constraints_bool_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints boolean datatype"), U("NoConstraintsBoolean"), false, U("NcBoolean"), value::null()); + const auto no_constraints_int16_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints int16 datatype"), U("NoConstraintsInt16"), false, U("NcInt16"), value::null()); + const auto no_constraints_int32_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints int32 datatype"), U("NoConstraintsInt32"), false, U("NcInt32"), value::null()); + const auto no_constraints_int64_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints int64 datatype"), U("NoConstraintsInt64"), false, U("NcInt64"), value::null()); + const auto no_constraints_uint16_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints uint16 datatype"), U("NoConstraintsUint16"), false, U("NcUint16"), value::null()); + const auto no_constraints_uint32_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints uint32 datatype"), U("NoConstraintsUint32"), false, U("NcUint32"), value::null()); + const auto no_constraints_uint64_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints uint64 datatype"), U("NoConstraintsUint64"), false, U("NcUint64"), value::null()); + const auto no_constraints_float32_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints float32 datatype"), U("NoConstraintsFloat32"), false, U("NcFloat32"), value::null()); + const auto no_constraints_float64_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints float64 datatype"), U("NoConstraintsFloat64"), false, U("NcFloat64"), value::null()); + const auto no_constraints_string_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints string datatype"), U("NoConstraintsString"), false, U("NcString"), value::null()); + const auto with_constraints_string_datatype = nmos::details::make_nc_datatype_typedef(U("With constraints string datatype"), U("WithConstraintsString"), false, U("NcString"), datatype_string_constraints); + const auto with_constraints_int32_datatype = nmos::details::make_nc_datatype_typedef(U("With constraints int32 datatype"), U("WithConstraintsInt32"), false, U("NcInt32"), datatype_int32_constraints); + + enum enum_value { foo, bar, baz }; + auto items = value::array(); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("foo"), U("foo"), enum_value::foo)); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("bar"), U("bar"), enum_value::bar)); + web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("baz"), U("baz"), enum_value::baz)); + const auto enum_datatype = nmos::details::make_nc_datatype_descriptor_enum(U("enum datatype"), U("enumDatatype"), items, value::null()); // no datatype constraints for enum datatype + + auto simple_struct_fields = value::array(); + web::json::push_back(simple_struct_fields, nmos::details::make_nc_field_descriptor(U("simple enum property example"), U("simpleEnumProperty"), U("enumDatatype"), false, false, value::null())); // no field constraints for enum field, as it is already described by its type + web::json::push_back(simple_struct_fields, nmos::details::make_nc_field_descriptor(U("simple string property example"), U("simpleStringProperty"), U("NcString"), false, false, datatype_string_constraints)); + web::json::push_back(simple_struct_fields, nmos::details::make_nc_field_descriptor(U("simple number property example"), U("simpleNumberProperty"), U("NcInt32"), false, false, datatype_int32_constraints)); + web::json::push_back(simple_struct_fields, nmos::details::make_nc_field_descriptor(U("simle boolean property example"), U("simpleBooleanProperty"), U("NcBoolean"), false, false, value::null())); // no field constraints for boolean field, as it is already described by its type + const auto simple_struct_datatype = nmos::details::make_nc_datatype_descriptor_struct(U("simple struct datatype"), U("simpleStructDatatype"), simple_struct_fields, value::null()); // no datatype constraints for struct datatype + + auto fields = value::array(); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Enum property example"), U("enumProperty"), U("enumDatatype"), false, false, value::null())); // no field constraints for enum field, as it is already described by its type + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("String property example"), U("stringProperty"), U("NcString"), false, false, datatype_string_constraints)); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Number property example"), U("numberProperty"), U("NcInt32"), false, false, datatype_int32_constraints)); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Boolean property example"), U("booleanProperty"), U("NcBoolean"), false, false, value::null())); // no field constraints for boolean field, as it is already described by its type + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Struct property example"), U("structProperty"), U("simpleStructDatatype"), false, false, value::null())); // no datatype constraints for struct datatype + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Sequence enum property example"), U("sequenceEnumProperty"), U("enumDatatype"), false, false, value::null())); // no field constraints for enum field, as it is already described by its type + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Sequence string property example"), U("sequenceStringProperty"), U("NcString"), false, false, datatype_string_constraints)); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Sequence number property example"), U("sequenceNumberProperty"), U("NcInt32"), false, false, datatype_int32_constraints)); + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Sequence boolean property example"), U("sequenceBooleanProperty"), U("NcBoolean"), false, false, value::null())); // no field constraints for boolean field, as it is already described by its type + web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Sequence struct property example"), U("sequenceStructProperty"), U("simpleStructDatatype"), false, false, value::null())); // no field constraints for struct field + const auto struct_datatype = nmos::details::make_nc_datatype_descriptor_struct(U("struct datatype"), U("structDatatype"), fields, value::null()); // no datatype constraints for struct datatype + + // setup datatypes in control_protocol_state + nmos::experimental::control_protocol_state control_protocol_state; + control_protocol_state.insert(nmos::experimental::datatype{ no_constraints_int16_datatype }); + control_protocol_state.insert(nmos::experimental::datatype{ no_constraints_int32_datatype }); + control_protocol_state.insert(nmos::experimental::datatype{ no_constraints_int64_datatype }); + control_protocol_state.insert(nmos::experimental::datatype{ no_constraints_uint16_datatype }); + control_protocol_state.insert(nmos::experimental::datatype{ no_constraints_uint32_datatype }); + control_protocol_state.insert(nmos::experimental::datatype{ no_constraints_uint64_datatype }); + control_protocol_state.insert(nmos::experimental::datatype{ no_constraints_string_datatype }); + control_protocol_state.insert(nmos::experimental::datatype{ with_constraints_int32_datatype }); + control_protocol_state.insert(nmos::experimental::datatype{ with_constraints_string_datatype }); + control_protocol_state.insert(nmos::experimental::datatype{ enum_datatype }); + control_protocol_state.insert(nmos::experimental::datatype{ simple_struct_datatype }); + control_protocol_state.insert(nmos::experimental::datatype{ struct_datatype }); // test get_runtime_property_constraints BST_REQUIRE_EQUAL(nmos::details::get_runtime_property_constraints(property_string_id, runtime_property_constraints), runtime_property_string_constraints); - BST_REQUIRE_EQUAL(nmos::details::get_runtime_property_constraints(property_number_id, runtime_property_constraints), runtime_property_number_constraints); + BST_REQUIRE_EQUAL(nmos::details::get_runtime_property_constraints(property_int32_id, runtime_property_constraints), runtime_property_int32_constraints); BST_REQUIRE_EQUAL(nmos::details::get_runtime_property_constraints(unknown_property_id, runtime_property_constraints), value::null()); // string property constraints validation - BST_REQUIRE(nmos::constraints_validation(value::string(U("1234567890")), runtime_property_string_constraints, property_string_constraints, datatype_string_constraints)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("12345678901")), runtime_property_string_constraints, property_string_constraints, datatype_string_constraints), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("123456789A")), runtime_property_string_constraints, property_string_constraints, datatype_string_constraints), false); - BST_REQUIRE(nmos::constraints_validation(value::string(U("12345678901")), value::null(), value::null(), value::null())); - BST_REQUIRE(nmos::constraints_validation(value::string(U("123456789A")), value::null(), value::null(), value::null())); - - BST_REQUIRE(nmos::constraints_validation(value::string(U("abcde")), value::null(), property_string_constraints, datatype_string_constraints)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("abcdef")), value::null(), property_string_constraints, datatype_string_constraints), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("abcd1")), value::null(), property_string_constraints, datatype_string_constraints), false); - BST_REQUIRE(nmos::constraints_validation(value::string(U("1a")), value::null(), value::null(), datatype_string_constraints)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("1a2")), value::null(), value::null(), datatype_string_constraints), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("1*")), value::null(), value::null(), datatype_string_constraints), false); + // runtime property constraints validation + const nmos::datatype_constraints_validation_parameters with_constraints_string_constraints_validation_params{ with_constraints_string_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE(nmos::constraints_validation(value::string(U("1234567890")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("12345678901")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("123456789A")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); + // property constraints validation + BST_REQUIRE(nmos::constraints_validation(value::string(U("abcde")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("abcdef")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("abcd1")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); + // datatype constraints validation + BST_REQUIRE(nmos::constraints_validation(value::string(U("1a")), value::null(), value::null(), with_constraints_string_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("1a2")), value::null(), value::null(), with_constraints_string_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("1*")), value::null(), value::null(), with_constraints_string_constraints_validation_params), false); + const nmos::datatype_constraints_validation_parameters no_constraints_string_constraints_validation_params{ no_constraints_string_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE(nmos::constraints_validation(value::string(U("1234567890-abcde-!\"£$%^&*()_+=")), value::null(), value::null(), no_constraints_string_constraints_validation_params)); // number property constraints validation - BST_REQUIRE(nmos::constraints_validation(10, runtime_property_number_constraints, property_number_constraints, datatype_number_constraints)); - BST_REQUIRE(nmos::constraints_validation(1000, runtime_property_number_constraints, property_number_constraints, datatype_number_constraints)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(9, runtime_property_number_constraints, property_number_constraints, datatype_number_constraints), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(1001, runtime_property_number_constraints, property_number_constraints, datatype_number_constraints), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(0.5, runtime_property_number_constraints, property_number_constraints, datatype_number_constraints), false); - BST_REQUIRE(nmos::constraints_validation(9, value::null(), value::null(), value::null())); - BST_REQUIRE(nmos::constraints_validation(1001, value::null(), value::null(), value::null())); - BST_REQUIRE(nmos::constraints_validation(0.5, value::null(), value::null(), value::null())); - - BST_REQUIRE(nmos::constraints_validation(50, value::null(), property_number_constraints, datatype_number_constraints)); - BST_REQUIRE(nmos::constraints_validation(500, value::null(), property_number_constraints, datatype_number_constraints)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(45, value::null(), property_number_constraints, datatype_number_constraints), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(505, value::null(), property_number_constraints, datatype_number_constraints), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(499, value::null(), property_number_constraints, datatype_number_constraints), false); - - BST_REQUIRE(nmos::constraints_validation(100, value::null(), value::null(), datatype_number_constraints)); - BST_REQUIRE(nmos::constraints_validation(250, value::null(), value::null(), datatype_number_constraints)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(90, value::null(), value::null(), datatype_number_constraints), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(260, value::null(), value::null(), datatype_number_constraints), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(99, value::null(), value::null(), datatype_number_constraints), false); + + // runtime property constraints validation + const nmos::datatype_constraints_validation_parameters with_constraints_int32_constraints_validation_params{ with_constraints_int32_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE(nmos::constraints_validation(10, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE(nmos::constraints_validation(1000, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(9, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(1001, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + // property constraints validation + BST_REQUIRE(nmos::constraints_validation(50, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE(nmos::constraints_validation(500, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(45, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(505, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(499, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + // datatype constraints validation + BST_REQUIRE(nmos::constraints_validation(100, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); + BST_REQUIRE(nmos::constraints_validation(250, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(90, value::null(), value::null(), with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(260, value::null(), value::null(), with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(99, value::null(), value::null(), with_constraints_int32_constraints_validation_params), false); + // int16 datatype constraints validation + const nmos::datatype_constraints_validation_parameters no_constraints_int16_constraints_validation_params{ no_constraints_int16_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), false); + BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); + BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); + // int32 datatype constraints validation + const nmos::datatype_constraints_validation_parameters no_constraints_int32_constraints_validation_params{ no_constraints_int32_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), false); + BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); + BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); + // int64 datatype constraints validation + const nmos::datatype_constraints_validation_parameters no_constraints_int64_constraints_validation_params{ no_constraints_int64_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), false); + BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); + BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); + // uint16 datatype constraints validation + const nmos::datatype_constraints_validation_parameters no_constraints_uint16_constraints_validation_params{ no_constraints_uint16_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::constraints_validation(-1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), false); + BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); + BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); + // uint32 datatype constraints validation + const nmos::datatype_constraints_validation_parameters no_constraints_uint32_constraints_validation_params{ no_constraints_uint32_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::constraints_validation(-1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), false); + BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); + BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); + // uint64 datatype constraints validation + const nmos::datatype_constraints_validation_parameters no_constraints_uint64_constraints_validation_params{ no_constraints_uint64_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::constraints_validation(-1, value::null(), value::null(), no_constraints_uint64_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), false); + BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); + BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); + // float32 datatype constraints validation + const nmos::datatype_constraints_validation_parameters no_constraints_float32_constraints_validation_params{ no_constraints_float32_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), false); + BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); + BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); + // float64 datatype constraints validation + const nmos::datatype_constraints_validation_parameters no_constraints_float64_constraints_validation_params{ no_constraints_float64_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::constraints_validation(1000, value::null(), value::null(), no_constraints_float64_constraints_validation_params), false); + BST_REQUIRE(nmos::constraints_validation(1000.0, value::null(), value::null(), no_constraints_float64_constraints_validation_params)); + BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); + BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); + // enum property datatype constraints validation + const nmos::datatype_constraints_validation_parameters enum_constraints_validation_params{ enum_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE(nmos::constraints_validation(enum_value::foo, value::null(), value::null(), enum_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(4, value::null(), value::null(), enum_constraints_validation_params), false); + + // struct property datatype constraints validation + const auto good_struct = value_of({ + { U("enumProperty"), enum_value::baz }, + { U("stringProperty"), U("xy") }, + { U("numberProperty"), 100 }, + { U("booleanProperty"), true }, + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), false } + }) }) } + }); + // missing field + const auto bad_struct1 = value_of({ + { U("stringProperty"), U("xy") }, + { U("numberProperty"), 100 }, + { U("booleanProperty"), true }, + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), false } + }) }) } + }); + // invalid fields + const auto bad_struct2 = value_of({ + { U("enumProperty"), 3 }, // bad value + { U("stringProperty"), U("xy") }, + { U("numberProperty"), 100 }, + { U("booleanProperty"), true }, + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), false } + }) }) } + }); + const auto bad_struct2_1 = value_of({ + { U("enumProperty"), enum_value::foo }, + { U("stringProperty"), U("xyz") }, // bad value + { U("numberProperty"), 100 }, + { U("booleanProperty"), true }, + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), false } + }) }) } + }); + const auto bad_struct2_2 = value_of({ + { U("enumProperty"), enum_value::foo }, + { U("stringProperty"), U("x£") }, // bad value + { U("numberProperty"), 100 }, + { U("booleanProperty"), true }, + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), false } + }) }) } + }); + const auto bad_struct2_3 = value_of({ + { U("enumProperty"), enum_value::foo }, + { U("stringProperty"), U("xy") }, + { U("numberProperty"), 99 }, // bad value + { U("booleanProperty"), true }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), false } + }) }) } + }); + const auto bad_struct2_4 = value_of({ + { U("enumProperty"), enum_value::foo }, + { U("stringProperty"), U("xy") }, + { U("numberProperty"), 100 }, + { U("booleanProperty"), 0 }, // bad value + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), false } + }) }) } + }); + const auto bad_struct2_5 = value_of({ + { U("enumProperty"), enum_value::foo }, + { U("stringProperty"), U("xy") }, + { U("numberProperty"), 100 }, + { U("booleanProperty"), true }, + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), 3 }, // bad value + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), false } + }) }) } + }); + const auto bad_struct2_5_1 = value_of({ + { U("enumProperty"), enum_value::foo }, + { U("stringProperty"), U("xy") }, + { U("numberProperty"), 100 }, + { U("booleanProperty"), true }, + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xyz") }, // bad value + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), false } + }) }) } + }); + const auto bad_struct2_5_2 = value_of({ + { U("enumProperty"), enum_value::foo }, + { U("stringProperty"), U("xy") }, + { U("numberProperty"), 100 }, + { U("booleanProperty"), true }, + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 99 }, // bad value + { U("simpleBooleanProperty"), true } + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), false } + }) }) } + }); + const auto bad_struct2_5_3 = value_of({ + { U("enumProperty"), enum_value::foo }, + { U("stringProperty"), U("xy") }, + { U("numberProperty"), 100 }, + { U("booleanProperty"), true }, + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), 3 } // bad value + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), false } + }) }) } + }); + const auto bad_struct2_6 = value_of({ + { U("enumProperty"), enum_value::foo }, + { U("stringProperty"), U("xy") }, + { U("numberProperty"), 100 }, + { U("booleanProperty"), true }, + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar, 4 }) }, // bad value + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), false } + }) }) } + }); + const auto bad_struct2_6_1 = value_of({ + { U("enumProperty"), enum_value::foo }, + { U("stringProperty"), U("xy") }, + { U("numberProperty"), 100 }, + { U("booleanProperty"), true }, + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bbb") }) }, // bad value + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), false } + }) }) } + }); + const auto bad_struct2_6_2 = value_of({ + { U("enumProperty"), enum_value::foo }, + { U("stringProperty"), U("xy") }, + { U("numberProperty"), 100 }, + { U("booleanProperty"), true }, + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 99, 110 }) }, // bad value + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), false } + }) }) } + }); + const auto bad_struct2_6_3 = value_of({ + { U("enumProperty"), enum_value::foo }, + { U("stringProperty"), U("xy") }, + { U("numberProperty"), 100 }, + { U("booleanProperty"), true }, + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, 0 }) }, // bad value + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), false } + }) }) } + }); + const auto bad_struct2_7 = value_of({ + { U("enumProperty"), enum_value::foo }, + { U("stringProperty"), U("xy") }, + { U("numberProperty"), 100 }, + { U("booleanProperty"), true }, + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), 3 }, // bad value + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), false } + }) }) } + }); + const auto bad_struct2_7_1 = value_of({ + { U("enumProperty"), enum_value::foo }, + { U("stringProperty"), U("xy") }, + { U("numberProperty"), 100 }, + { U("booleanProperty"), true }, + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("abc") }, // bad value + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), false } + }) }) } + }); + const auto bad_struct2_7_2 = value_of({ + { U("enumProperty"), enum_value::foo }, + { U("stringProperty"), U("xy") }, + { U("numberProperty"), 100 }, + { U("booleanProperty"), true }, + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 251 }, // bad value + { U("simpleBooleanProperty"), false } + }) }) } + }); + const auto bad_struct2_7_3 = value_of({ + { U("enumProperty"), enum_value::foo }, + { U("stringProperty"), U("xy") }, + { U("numberProperty"), 100 }, + { U("booleanProperty"), true }, + { U("structProperty"), value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }) }, + { U("sequenceEnumProperty"), value_of({ enum_value::foo, enum_value::bar }) }, + { U("sequenceStringProperty"), value_of({ U("aa"), U("bb") }) }, + { U("sequenceNumberProperty"), value_of({ 100, 110 }) }, + { U("sequenceBooleanProperty"), value_of({ true, false }) }, + { U("sequenceStructProperty"), value_of({ + value_of({ + { U("simpleEnumProperty"), enum_value::bar }, + { U("simpleStringProperty"), U("xy") }, + { U("simpleNumberProperty"), 100 }, + { U("simpleBooleanProperty"), true } + }), value_of({ + { U("simpleEnumProperty"), enum_value::foo }, + { U("simpleStringProperty"), U("ab") }, + { U("simpleNumberProperty"), 200 }, + { U("simpleBooleanProperty"), 0 } // bad value + }) }) } + }); + + const nmos::datatype_constraints_validation_parameters struct_constraints_validation_params{ struct_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE(nmos::constraints_validation(good_struct, value::null(), value::null(), struct_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct1, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_1, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_2, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_3, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_4, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_5, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_5_1, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_5_2, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_5_3, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_6, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_6_1, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_6_2, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_6_3, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_7, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_7_1, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_7_2, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_7_3, value::null(), value::null(), struct_constraints_validation_params), false); } From 0049eaa764a824972e118a24b5191822346bac1f Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 14 Nov 2023 15:30:49 +0000 Subject: [PATCH 072/250] Return property_deprecated(298) if property is marked as deprecated --- Development/nmos/control_protocol_methods.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index feba52442..c1426a3ab 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -26,7 +26,7 @@ namespace nmos const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); if (!property.is_null()) { - return make_control_protocol_message_response(handle, { nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(property))); + return make_control_protocol_message_response(handle, { nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(property))); } // unknown property @@ -77,7 +77,7 @@ namespace nmos }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::value_changed, val } })); - return make_control_protocol_message_response(handle, { nc_method_status::ok }); + return make_control_protocol_message_response(handle, { nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); } // unknown property @@ -112,7 +112,7 @@ namespace nmos if (data.as_array().size() > (size_t)index) { - return make_control_protocol_message_response(handle, { nc_method_status::ok }, data.at(index)); + return make_control_protocol_message_response(handle, { nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, data.at(index)); } // out of bound @@ -173,7 +173,7 @@ namespace nmos }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::sequence_item_changed, val, nc_id(index) } })); - return make_control_protocol_message_response(handle, { nc_method_status::ok }); + return make_control_protocol_message_response(handle, { nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); } // out of bound @@ -237,7 +237,7 @@ namespace nmos }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::sequence_item_added, val, sequence_item_index } })); - return make_control_protocol_message_response(handle, { nc_method_status::ok }, sequence_item_index); + return make_control_protocol_message_response(handle, { nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, sequence_item_index); } // unknown property @@ -279,7 +279,7 @@ namespace nmos }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_removed, nc_id(index) } })); - return make_control_protocol_message_response(handle, { nc_method_status::ok }); + return make_control_protocol_message_response(handle, { nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); } // out of bound @@ -325,7 +325,7 @@ namespace nmos if (data.is_null()) { // null - return make_control_protocol_message_response(handle, { nc_method_status::ok }, value::null()); + return make_control_protocol_message_response(handle, { nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, value::null()); } } else From cf492a4d2265b145e9a90d76fae5363363637ad0 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 14 Nov 2023 18:49:12 +0000 Subject: [PATCH 073/250] Code tidy-up --- Development/nmos/control_protocol_methods.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index c1426a3ab..c5f53037f 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -64,7 +64,7 @@ namespace nmos // do constraints validation if (!val.is_null()) { - if (!constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) + if (!nmos::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) { return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); } @@ -100,7 +100,7 @@ namespace nmos const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); if (!property.is_null()) { - auto& data = resource->data.at(nmos::fields::nc::name(property)); + const auto& data = resource->data.at(nmos::fields::nc::name(property)); if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) { @@ -161,7 +161,7 @@ namespace nmos if (data.as_array().size() > (size_t)index) { // do constraints validation - if (!constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) + if (!nmos::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) { return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); } @@ -223,7 +223,7 @@ namespace nmos const nc_id sequence_item_index = data.is_null() ? 0 : nc_id(data.as_array().size()); // do constraints validation - if (!constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) + if (!nmos::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) { return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); } @@ -260,7 +260,7 @@ namespace nmos const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); if (!property.is_null()) { - auto& data = resource->data.at(nmos::fields::nc::name(property)); + const auto& data = resource->data.at(nmos::fields::nc::name(property)); if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) { @@ -317,7 +317,7 @@ namespace nmos return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); } - auto& data = resource->data.at(nmos::fields::nc::name(property)); + const auto& data = resource->data.at(nmos::fields::nc::name(property)); if (nmos::fields::nc::is_nullable(property)) { From 93f69c2325cd6a67d5c40bc63bb0d7d15477c3c6 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 14 Nov 2023 19:34:56 +0000 Subject: [PATCH 074/250] Enhance non-standard example control method handlers, add level 2 and level 0 constraints implementation to example control --- .../nmos-cpp-node/node_implementation.cpp | 92 +++++++++++++------ 1 file changed, 63 insertions(+), 29 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 31ad30004..f582d239d 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -977,13 +977,19 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr Gamma = 3 }; { + // following constraints are used for the example control class level 0 datatype, level 1 property constraints and the method parameters constraints + auto make_string_example_argument_constraints = []() {return nmos::details::make_nc_parameter_constraints_string(10, U("^[a-z]+$")); }; + auto make_number_example_argument_constraints = []() {return nmos::details::make_nc_parameter_constraints_number(0, 1000, 1); }; + // Example control class properties std::vector example_control_properties = { nmos::experimental::make_control_class_property(U("Example enum property"), { 3, 1 }, enum_property, U("ExampleEnum")), // create "Example string property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - nmos::experimental::make_control_class_property(U("Example string property"), { 3, 2 }, string_property, U("NcString"), false, false, false, false, nmos::details::make_nc_parameter_constraints_string(10)), + // use nmos::details::make_nc_parameter_constraints_string to create datatype constraints + nmos::experimental::make_control_class_property(U("Example string property"), { 3, 2 }, string_property, U("NcString"), false, false, false, false, make_string_example_argument_constraints()), // create "Example numeric property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - nmos::experimental::make_control_class_property(U("Example numeric property"), { 3, 3 }, number_property, U("NcUint64"), false, false, false, false, nmos::details::make_nc_parameter_constraints_number(0, 1000, 1)), + // use nmos::details::make_nc_parameter_constraints_number to create datatype constraints + nmos::experimental::make_control_class_property(U("Example numeric property"), { 3, 3 }, number_property, U("NcUint64"), false, false, false, false, make_number_example_argument_constraints()), nmos::experimental::make_control_class_property(U("Example boolean property"), { 3, 4 }, boolean_property, U("NcBoolean")), nmos::experimental::make_control_class_property(U("Example object property"), { 3, 5 }, object_property, U("ExampleDataType")), nmos::experimental::make_control_class_property(U("Method no args invoke counter"), { 3, 6 }, method_no_args_count, U("NcUint64"), true), @@ -996,10 +1002,6 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr nmos::experimental::make_control_class_property(U("Example object sequence property"), { 3, 13 }, object_sequence, U("ExampleDataType"), false, false, true) }; - // Example control class method handlers - auto make_string_example_argument_constraints = []() {return nmos::details::make_nc_parameter_constraints_string(80); }; - auto make_number_example_argument_constraints = []() {return nmos::details::make_nc_parameter_constraints_number(100, 1000, 1); }; - auto example_method_with_no_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -1012,32 +1014,64 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - slog::log(gate, SLOG_FLF) << "Executing the example method with simple arguments:" - << " enum_arg: " - << enum_arg(arguments).to_int32() - << " string_arg: " - << string_arg(arguments) - << " number_arg: " - << number_arg(arguments).to_uint64() - << " boolean_arg: " - << boolean_arg(arguments); + using web::json::value; + + slog::log(gate, SLOG_FLF) << "Executing the example method with simple arguments: " << arguments.serialize(); // example to do method arguments constraints validation - if (!nmos::constraints_validation(arguments.at(string_arg), make_string_example_argument_constraints()) - || !nmos::constraints_validation(arguments.at(number_arg), make_number_example_argument_constraints())) + if (!nmos::constraints_validation(arguments.at(enum_arg), value::null(), value::null(), { nmos::details::get_datatype_descriptor(value::string(U("ExampleEnum")), get_control_protocol_datatype), get_control_protocol_datatype })) { + slog::log(gate, SLOG_FLF) << "invalid enum_arg: " << arguments.at(enum_arg).serialize(); + return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); + } + if (!nmos::constraints_validation(arguments.at(string_arg), make_string_example_argument_constraints(), value::null(), {nmos::details::get_datatype_descriptor(value::string(U("NcString")), get_control_protocol_datatype), get_control_protocol_datatype})) + { + slog::log(gate, SLOG_FLF) << "invalid string_arg: " << arguments.at(string_arg).serialize(); + return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); + } + if (!nmos::constraints_validation(arguments.at(number_arg), make_number_example_argument_constraints(), value::null(), { nmos::details::get_datatype_descriptor(value::string(U("NcUint64")), get_control_protocol_datatype), get_control_protocol_datatype })) + { + slog::log(gate, SLOG_FLF) << "invalid number_arg: " << arguments.at(number_arg).serialize(); + return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); + } + if (!nmos::constraints_validation(arguments.at(boolean_arg), value::null(), value::null(), { nmos::details::get_datatype_descriptor(value::string(U("NcBoolean")), get_control_protocol_datatype), get_control_protocol_datatype })) + { + slog::log(gate, SLOG_FLF) << "invalid boolean_arg: " << arguments.at(boolean_arg).serialize(); return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); } return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::ok }); }; - auto example_method_with_object_args = [obj_arg](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + auto example_method_with_object_args = [obj_arg, enum_arg, string_arg, number_arg, boolean_arg, make_string_example_argument_constraints, make_number_example_argument_constraints](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - slog::log(gate, SLOG_FLF) << "Executing the example method with object argument:" - << " obj_arg: " - << obj_arg(arguments).serialize(); + using web::json::value; + + slog::log(gate, SLOG_FLF) << "Executing the example method with object argument: " << obj_arg(arguments).serialize(); + + // example to do method arguments constraints validation + const auto& obj_arg_ = obj_arg(arguments); + if (!nmos::constraints_validation(obj_arg_.at(enum_arg), value::null(), value::null(), { nmos::details::get_datatype_descriptor(value::string(U("ExampleEnum")), get_control_protocol_datatype), get_control_protocol_datatype })) + { + slog::log(gate, SLOG_FLF) << "invalid enum_arg: " << obj_arg_.at(enum_arg).serialize(); + return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); + } + if (!nmos::constraints_validation(obj_arg_.at(string_arg), make_string_example_argument_constraints(), value::null(), { nmos::details::get_datatype_descriptor(value::string(U("NcString")), get_control_protocol_datatype), get_control_protocol_datatype })) + { + slog::log(gate, SLOG_FLF) << "invalid string_arg: " << obj_arg_.at(string_arg).serialize(); + return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); + } + if (!nmos::constraints_validation(obj_arg_.at(number_arg), make_number_example_argument_constraints(), value::null(), { nmos::details::get_datatype_descriptor(value::string(U("NcUint64")), get_control_protocol_datatype), get_control_protocol_datatype })) + { + slog::log(gate, SLOG_FLF) << "invalid number_arg: " << obj_arg_.at(number_arg).serialize(); + return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); + } + if (!nmos::constraints_validation(obj_arg_.at(boolean_arg), value::null(), value::null(), { nmos::details::get_datatype_descriptor(value::string(U("NcBoolean")), get_control_protocol_datatype), get_control_protocol_datatype })) + { + slog::log(gate, SLOG_FLF) << "invalid boolean_arg: " << obj_arg_.at(boolean_arg).serialize(); + return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); + } return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::ok }); }; @@ -1089,13 +1123,13 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr { // level 0: datatype constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html // use nmos::details::make_nc_parameter_constraints_string to create datatype constraints - value datatype_constraints = value::null(); + value datatype_constraints = make_string_example_argument_constraints(); web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("String property example"), string_property, U("NcString"), false, false, datatype_constraints)); } { // level 0: datatype constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html // use nmos::details::make_nc_parameter_constraints_number to create datatype constraints - value datatype_constraints = value::null(); + value datatype_constraints = make_number_example_argument_constraints(); web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Number property example"), number_property, U("NcUint64"), false, false, datatype_constraints)); } web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Boolean property example"), boolean_property, U("NcBoolean"), false, false, value::null())); @@ -1233,12 +1267,12 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // example example-control auto example_control = make_example_control(++oid, nmos::root_block_oid, U("ExampleControl"), U("Example control worker"), U("Example control worker"), value::null(), - value::null(), // specify the level 2: runtime constraints, see https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use of make_nc_property_constraints_string and make_nc_property_constraints_number to create runtime constraints - // e.g. value_of({ - // { nmos::details::make_nc_property_constraints_string({3, 2}, 10) }, - // { nmos::details::make_nc_property_constraints_number({3, 3}, 10, 100, 2) } - // }), + // specify the level 2: runtime constraints, see https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + // use of make_nc_property_constraints_string and make_nc_property_constraints_number to create runtime constraints + value_of({ + { nmos::details::make_nc_property_constraints_string({3, 2}, 5, U("^[a-z]+$")) }, + { nmos::details::make_nc_property_constraints_number({3, 3}, 10, 100, 2) } + }), example_enum::Undefined, U("test"), 3, From d26bc325987bcd0da32496575ac92c2a13c76f85 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 14 Nov 2023 20:36:52 +0000 Subject: [PATCH 075/250] Prevent comparsion warning --- Development/nmos/control_protocol_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index d1f2f933f..f1f2f56b5 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -117,7 +117,7 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring if (constraints.has_field(nmos::fields::nc::max_characters) && !constraints.at(nmos::fields::nc::max_characters).is_null()) { - const auto max_characters = nmos::fields::nc::max_characters(constraints); + const size_t max_characters = nmos::fields::nc::max_characters(constraints); if (!value.is_string() || value.as_string().length() > max_characters) { return false; } } if (constraints.has_field(nmos::fields::nc::pattern) && !constraints.at(nmos::fields::nc::pattern).is_null()) From 2471b019f16a3e0c88f1407f8b5bd549e3dae3fb Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 15 Nov 2023 18:46:42 +0000 Subject: [PATCH 076/250] Reject Set on non-sequence value to sequence property --- Development/nmos/control_protocol_methods.cpp | 1 + Development/nmos/control_protocol_utils.cpp | 94 +++++++++++++------ .../nmos/test/control_protocol_test.cpp | 19 +++- 3 files changed, 82 insertions(+), 32 deletions(-) diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index c5f53037f..d3e4e8196 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -56,6 +56,7 @@ namespace nmos } if ((val.is_null() && !nmos::fields::nc::is_nullable(property)) + || (!val.is_array() && nmos::fields::nc::is_sequence(property)) || (val.is_array() && !nmos::fields::nc::is_sequence(property))) { return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index f1f2f56b5..3e34bc034 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -138,44 +138,76 @@ namespace nmos { const auto& datatype_type = nmos::fields::nc::type(params.datatype_descriptor); - auto is_int16 = [](int32_t value) - { - return value >= (std::numeric_limits::min)() - && value <= (std::numeric_limits::max)(); - }; - auto is_uint16 = [](uint32_t value) - { - return value >= (std::numeric_limits::min)() - && value <= (std::numeric_limits::max)(); - }; - auto is_float32 = [](double value) - { - return value >= (std::numeric_limits::min)() - && value <= (std::numeric_limits::max)(); - }; - // do NcDatatypeDescriptorPrimitive constraints validation if (nc_datatype_type::Primitive == datatype_type) { // hmm, for the primitive type, it should not have datatype constraints specified via the datatype_descriptor but just in case const auto& datatype_constraints = nmos::fields::nc::constraints(params.datatype_descriptor); - if (!datatype_constraints.is_null()) { return constraints_validation(data, datatype_constraints); } + if (!datatype_constraints.is_null()) + { + if (data.is_array()) + { + for (const auto& val : data.as_array()) + { + if (!constraints_validation(val, datatype_constraints)) { return false; } + } + // reaching here, validation successfully + return true; + } + else + { + return constraints_validation(data, datatype_constraints); + } + } - // do primitive type constraints + auto primitive_validation = [](const nc_name& name, const web::json::value& val) + { + auto is_int16 = [](int32_t value) + { + return value >= (std::numeric_limits::min)() + && value <= (std::numeric_limits::max)(); + }; + auto is_uint16 = [](uint32_t value) + { + return value >= (std::numeric_limits::min)() + && value <= (std::numeric_limits::max)(); + }; + auto is_float32 = [](double value) + { + return value >= (std::numeric_limits::min)() + && value <= (std::numeric_limits::max)(); + }; + + if (U("NcBoolean") == name) { return val.is_boolean(); } + if (U("NcInt16") == name && val.is_number()) { return is_int16(val.as_number().to_int32()); } + if (U("NcInt32") == name && val.is_number()) { return val.as_number().is_int32(); } + if (U("NcInt64") == name && val.is_number()) { return val.as_number().is_int64(); } + if (U("NcUint16") == name && val.is_number()) { return is_uint16(val.as_number().to_uint32()); } + if (U("NcUint32") == name && val.is_number()) { return val.as_number().is_uint32(); } + if (U("NcUint64") == name && val.is_number()) { return val.as_number().is_uint64(); } + if (U("NcFloat32") == name && val.is_number()) { return is_float32(val.as_number().to_double()); } + if (U("NcFloat64") == name && val.is_number()) { return !val.as_number().is_integral(); } + if (U("NcString") == name) { return val.is_string(); } + + // invalid primitive type + return false; + }; + + // do primitive type constraints validation const auto& name = nmos::fields::nc::name(params.datatype_descriptor); - if (U("NcBoolean") == name) { return data.is_boolean(); } - if (U("NcInt16") == name && data.is_number()) { return is_int16(data.as_number().to_int32()); } - if (U("NcInt32") == name && data.is_number()) { return data.as_number().is_int32(); } - if (U("NcInt64") == name && data.is_number()) { return data.as_number().is_int64(); } - if (U("NcUint16") == name && data.is_number()) { return is_uint16(data.as_number().to_uint32()); } - if (U("NcUint32") == name && data.is_number()) { return data.as_number().is_uint32(); } - if (U("NcUint64") == name && data.is_number()) { return data.as_number().is_uint64(); } - if (U("NcFloat32") == name && data.is_number()) { return is_float32(data.as_number().to_double()); } - if (U("NcFloat64") == name && data.is_number()) { return !data.as_number().is_integral(); } - if (U("NcString") == name) { return data.is_string(); } - - // invalid primitive type - return false; + if (data.is_array()) + { + for (const auto& val : data.as_array()) + { + if (!primitive_validation(name, val)) { return false; } + } + // reaching here, validation successfully + return true; + } + else + { + return primitive_validation(name, data); + } } // do NcDatatypeDescriptorTypeDef constraints validation diff --git a/Development/nmos/test/control_protocol_test.cpp b/Development/nmos/test/control_protocol_test.cpp index 7ffd6d59f..fa13f3b83 100644 --- a/Development/nmos/test/control_protocol_test.cpp +++ b/Development/nmos/test/control_protocol_test.cpp @@ -742,6 +742,8 @@ BST_TEST_CASE(testConstraints) const auto no_constraints_string_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints string datatype"), U("NoConstraintsString"), false, U("NcString"), value::null()); const auto with_constraints_string_datatype = nmos::details::make_nc_datatype_typedef(U("With constraints string datatype"), U("WithConstraintsString"), false, U("NcString"), datatype_string_constraints); const auto with_constraints_int32_datatype = nmos::details::make_nc_datatype_typedef(U("With constraints int32 datatype"), U("WithConstraintsInt32"), false, U("NcInt32"), datatype_int32_constraints); + const auto no_constraints_int32_seq_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints int64 datatype"), U("NoConstraintsInt64"), true, U("NcInt32"), value::null()); + const auto no_constraints_string_seq_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints string datatype"), U("NoConstraintsString"), true, U("NcString"), value::null()); enum enum_value { foo, bar, baz }; auto items = value::array(); @@ -783,7 +785,8 @@ BST_TEST_CASE(testConstraints) control_protocol_state.insert(nmos::experimental::datatype{ with_constraints_string_datatype }); control_protocol_state.insert(nmos::experimental::datatype{ enum_datatype }); control_protocol_state.insert(nmos::experimental::datatype{ simple_struct_datatype }); - control_protocol_state.insert(nmos::experimental::datatype{ struct_datatype }); + control_protocol_state.insert(nmos::experimental::datatype{ no_constraints_int32_seq_datatype }); + control_protocol_state.insert(nmos::experimental::datatype{ no_constraints_string_seq_datatype }); // test get_runtime_property_constraints BST_REQUIRE_EQUAL(nmos::details::get_runtime_property_constraints(property_string_id, runtime_property_constraints), runtime_property_string_constraints); @@ -880,6 +883,20 @@ BST_TEST_CASE(testConstraints) const nmos::datatype_constraints_validation_parameters enum_constraints_validation_params{ enum_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; BST_REQUIRE(nmos::constraints_validation(enum_value::foo, value::null(), value::null(), enum_constraints_validation_params)); BST_REQUIRE_EQUAL(nmos::constraints_validation(4, value::null(), value::null(), enum_constraints_validation_params), false); + // invalid data vs primitive datatype constraints + const nmos::datatype_constraints_validation_parameters no_constraints_string_seq_constraints_validation_params{ no_constraints_string_seq_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE(nmos::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); + BST_REQUIRE(nmos::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")), value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(1, value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), false); + const nmos::datatype_constraints_validation_parameters no_constraints_int32_seq_constraints_validation_params{ no_constraints_int32_seq_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), false); + BST_REQUIRE(nmos::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); + BST_REQUIRE(nmos::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("1234567890-abcde-!\"£$%^&*()_+=")), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_string_constraints_validation_params), false); // struct property datatype constraints validation const auto good_struct = value_of({ From 4420e1eb5d9a5832b5ea68128c249c3896ac371b Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 15 Nov 2023 23:00:10 +0000 Subject: [PATCH 077/250] Fix runtime and property sequence constraints validation --- .../nmos-cpp-node/node_implementation.cpp | 12 +- Development/nmos/control_protocol_utils.cpp | 162 ++++++++---------- .../nmos/test/control_protocol_test.cpp | 14 ++ 3 files changed, 98 insertions(+), 90 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index f582d239d..1d439f423 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -985,20 +985,24 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr std::vector example_control_properties = { nmos::experimental::make_control_class_property(U("Example enum property"), { 3, 1 }, enum_property, U("ExampleEnum")), // create "Example string property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use nmos::details::make_nc_parameter_constraints_string to create datatype constraints + // use nmos::details::make_nc_parameter_constraints_string to create property constraints nmos::experimental::make_control_class_property(U("Example string property"), { 3, 2 }, string_property, U("NcString"), false, false, false, false, make_string_example_argument_constraints()), // create "Example numeric property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use nmos::details::make_nc_parameter_constraints_number to create datatype constraints + // use nmos::details::make_nc_parameter_constraints_number to create property constraints nmos::experimental::make_control_class_property(U("Example numeric property"), { 3, 3 }, number_property, U("NcUint64"), false, false, false, false, make_number_example_argument_constraints()), nmos::experimental::make_control_class_property(U("Example boolean property"), { 3, 4 }, boolean_property, U("NcBoolean")), nmos::experimental::make_control_class_property(U("Example object property"), { 3, 5 }, object_property, U("ExampleDataType")), nmos::experimental::make_control_class_property(U("Method no args invoke counter"), { 3, 6 }, method_no_args_count, U("NcUint64"), true), nmos::experimental::make_control_class_property(U("Method simple args invoke counter"), { 3, 7 }, method_simple_args_count, U("NcUint64"), true), nmos::experimental::make_control_class_property(U("Method obj arg invoke counter"), { 3, 8 }, method_object_arg_count, U("NcUint64"), true), - nmos::experimental::make_control_class_property(U("Example string sequence property"), { 3, 9 }, string_sequence, U("NcString"), false, false, true), + // create "Example sequence string property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + // use nmos::details::make_nc_parameter_constraints_string to create sequence property constraints + nmos::experimental::make_control_class_property(U("Example string sequence property"), { 3, 9 }, string_sequence, U("NcString"), false, false, true, false, make_string_example_argument_constraints()), nmos::experimental::make_control_class_property(U("Example boolean sequence property"), { 3, 10 }, boolean_sequence, U("NcBoolean"), false, false, true), nmos::experimental::make_control_class_property(U("Example enum sequence property"), { 3, 11 }, enum_sequence, U("ExampleEnum"), false, false, true), - nmos::experimental::make_control_class_property(U("Example number sequence property"), { 3, 12 }, number_sequence, U("NcUint64"), false, false, true), + // create "Example sequence numeric property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + // use nmos::details::make_nc_parameter_constraints_number to create sequence property constraints + nmos::experimental::make_control_class_property(U("Example number sequence property"), { 3, 12 }, number_sequence, U("NcUint64"), false, false, true, false, make_number_example_argument_constraints()), nmos::experimental::make_control_class_property(U("Example object sequence property"), { 3, 13 }, object_sequence, U("ExampleDataType"), false, false, true) }; diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 3e34bc034..07260eb60 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -77,59 +77,74 @@ namespace nmos // constraints validation // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring - bool constraints_validation(const web::json::value& value, const web::json::value& constraints) + bool constraints_validation(const web::json::value& data, const web::json::value& constraints) { - // is numeric constraints - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber - if (constraints.has_field(nmos::fields::nc::step) && !nmos::fields::nc::step(constraints).is_null()) + auto parameter_constraints_validation = [&constraints](const web::json::value& value) { - if (!value.is_integer()) { return false; } + // is numeric constraints + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber + if (constraints.has_field(nmos::fields::nc::step) && !nmos::fields::nc::step(constraints).is_null()) + { + if (!value.is_integer()) { return false; } - const auto step = nmos::fields::nc::step(constraints).as_double(); - if (step <= 0) { return false; } + const auto step = nmos::fields::nc::step(constraints).as_double(); + if (step <= 0) { return false; } - const auto value_double = value.as_double(); + const auto value_double = value.as_double(); + if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) + { + auto min = nmos::fields::nc::minimum(constraints).as_double(); + if (0 != std::fmod(value_double - min, step)) { return false; } + } + else if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) + { + auto max = nmos::fields::nc::maximum(constraints).as_double(); + if (0 != std::fmod(max - value_double, step)) { return false; } + } + else + { + if (0 != std::fmod(value_double, step)) { return false; } + } + } if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) { - auto min = nmos::fields::nc::minimum(constraints).as_double(); - if (0 != std::fmod(value_double - min, step)) { return false; } + if (!value.is_integer() || value.as_double() < nmos::fields::nc::minimum(constraints).as_double()) { return false; } } - else if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) + if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) { - auto max = nmos::fields::nc::maximum(constraints).as_double(); - if (0 != std::fmod(max - value_double, step)) { return false; } + if (!value.is_integer() || value.as_double() > nmos::fields::nc::maximum(constraints).as_double()) { return false; } } - else + + // is string constraints + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring + if (constraints.has_field(nmos::fields::nc::max_characters) && !constraints.at(nmos::fields::nc::max_characters).is_null()) { - if (0 != std::fmod(value_double, step)) { return false; } + const size_t max_characters = nmos::fields::nc::max_characters(constraints); + if (!value.is_string() || value.as_string().length() > max_characters) { return false; } + } + if (constraints.has_field(nmos::fields::nc::pattern) && !constraints.at(nmos::fields::nc::pattern).is_null()) + { + if (!value.is_string()) { return false; } + const auto value_string = utility::us2s(value.as_string()); + bst::regex pattern(utility::us2s(nmos::fields::nc::pattern(constraints))); + if (!bst::regex_match(value_string, pattern)) { return false; } } - } - if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) - { - if (!value.is_integer() || value.as_double() < nmos::fields::nc::minimum(constraints).as_double()) { return false; } - } - if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) - { - if (!value.is_integer() || value.as_double() > nmos::fields::nc::maximum(constraints).as_double()) { return false; } - } - // is string constraints - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring - if (constraints.has_field(nmos::fields::nc::max_characters) && !constraints.at(nmos::fields::nc::max_characters).is_null()) - { - const size_t max_characters = nmos::fields::nc::max_characters(constraints); - if (!value.is_string() || value.as_string().length() > max_characters) { return false; } - } - if (constraints.has_field(nmos::fields::nc::pattern) && !constraints.at(nmos::fields::nc::pattern).is_null()) + // reaching here, parameter validation successfully + return true; + }; + + if (data.is_array()) { - if (!value.is_string()) { return false; } - const auto value_string = utility::us2s(value.as_string()); - bst::regex pattern(utility::us2s(nmos::fields::nc::pattern(constraints))); - if (!bst::regex_match(value_string, pattern)) { return false; } + for (const auto& value : data.as_array()) + { + if (!parameter_constraints_validation(value)) { return false; } + } + // validation successfully + return true; } - // reaching here, no validation is required - return true; + return parameter_constraints_validation(data); } // level 0 datatype constraints validation @@ -145,22 +160,10 @@ namespace nmos const auto& datatype_constraints = nmos::fields::nc::constraints(params.datatype_descriptor); if (!datatype_constraints.is_null()) { - if (data.is_array()) - { - for (const auto& val : data.as_array()) - { - if (!constraints_validation(val, datatype_constraints)) { return false; } - } - // reaching here, validation successfully - return true; - } - else - { - return constraints_validation(data, datatype_constraints); - } + return constraints_validation(data, datatype_constraints); } - auto primitive_validation = [](const nc_name& name, const web::json::value& val) + auto primitive_validation = [](const nc_name& name, const web::json::value& value) { auto is_int16 = [](int32_t value) { @@ -178,16 +181,16 @@ namespace nmos && value <= (std::numeric_limits::max)(); }; - if (U("NcBoolean") == name) { return val.is_boolean(); } - if (U("NcInt16") == name && val.is_number()) { return is_int16(val.as_number().to_int32()); } - if (U("NcInt32") == name && val.is_number()) { return val.as_number().is_int32(); } - if (U("NcInt64") == name && val.is_number()) { return val.as_number().is_int64(); } - if (U("NcUint16") == name && val.is_number()) { return is_uint16(val.as_number().to_uint32()); } - if (U("NcUint32") == name && val.is_number()) { return val.as_number().is_uint32(); } - if (U("NcUint64") == name && val.is_number()) { return val.as_number().is_uint64(); } - if (U("NcFloat32") == name && val.is_number()) { return is_float32(val.as_number().to_double()); } - if (U("NcFloat64") == name && val.is_number()) { return !val.as_number().is_integral(); } - if (U("NcString") == name) { return val.is_string(); } + if (U("NcBoolean") == name) { return value.is_boolean(); } + if (U("NcInt16") == name && value.is_number()) { return is_int16(value.as_number().to_int32()); } + if (U("NcInt32") == name && value.is_number()) { return value.as_number().is_int32(); } + if (U("NcInt64") == name && value.is_number()) { return value.as_number().is_int64(); } + if (U("NcUint16") == name && value.is_number()) { return is_uint16(value.as_number().to_uint32()); } + if (U("NcUint32") == name && value.is_number()) { return value.as_number().is_uint32(); } + if (U("NcUint64") == name && value.is_number()) { return value.as_number().is_uint64(); } + if (U("NcFloat32") == name && value.is_number()) { return is_float32(value.as_number().to_double()); } + if (U("NcFloat64") == name && value.is_number()) { return !value.as_number().is_integral(); } + if (U("NcString") == name) { return value.is_string(); } // invalid primitive type return false; @@ -197,17 +200,15 @@ namespace nmos const auto& name = nmos::fields::nc::name(params.datatype_descriptor); if (data.is_array()) { - for (const auto& val : data.as_array()) + for (const auto& value : data.as_array()) { - if (!primitive_validation(name, val)) { return false; } + if (!primitive_validation(name, value)) { return false; } } - // reaching here, validation successfully + // reaching here, primitive validation successfully return true; } - else - { - return primitive_validation(name, data); - } + + return primitive_validation(name, data); } // do NcDatatypeDescriptorTypeDef constraints validation @@ -252,19 +253,8 @@ namespace nmos { auto value = data.at(name); - if (value.is_array()) - { - for (const auto& val : value.as_array()) - { - // do field constraints validation - if (!constraints_validation(val, constraints)) { return false; } - } - } - else - { - // do field constraints validation - if (!constraints_validation(value, constraints)) { return false; } - } + // do field constraints validation + if (!constraints_validation(value, constraints)) { return false; } } else { @@ -574,15 +564,15 @@ namespace nmos // multiple levels of constraints validation // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - bool constraints_validation(const web::json::value& value, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) + bool constraints_validation(const web::json::value& data, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) { // do level 2 runtime property constraints validation - if (!runtime_property_constraints.is_null()) { return details::constraints_validation(value, runtime_property_constraints); } + if (!runtime_property_constraints.is_null()) { return details::constraints_validation(data, runtime_property_constraints); } // do level 1 property constraints validation - if (!property_constraints.is_null()) { return details::constraints_validation(value, property_constraints); } + if (!property_constraints.is_null()) { return details::constraints_validation(data, property_constraints); } // do level 0 datatype constraints validation - return details::datatype_constraints_validation(value, params); + return details::datatype_constraints_validation(data, params); } } diff --git a/Development/nmos/test/control_protocol_test.cpp b/Development/nmos/test/control_protocol_test.cpp index fa13f3b83..381433923 100644 --- a/Development/nmos/test/control_protocol_test.cpp +++ b/Development/nmos/test/control_protocol_test.cpp @@ -800,10 +800,16 @@ BST_TEST_CASE(testConstraints) BST_REQUIRE(nmos::constraints_validation(value::string(U("1234567890")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("12345678901")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("123456789A")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); + BST_REQUIRE(nmos::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("1234567890")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("12345678901")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ value::string(U("1234567890")), 1 }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); // property constraints validation BST_REQUIRE(nmos::constraints_validation(value::string(U("abcde")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("abcdef")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("abcd1")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); + BST_REQUIRE(nmos::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcde")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcdef")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ value::string(U("abcde")), 1 }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); // datatype constraints validation BST_REQUIRE(nmos::constraints_validation(value::string(U("1a")), value::null(), value::null(), with_constraints_string_constraints_validation_params)); BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("1a2")), value::null(), value::null(), with_constraints_string_constraints_validation_params), false); @@ -819,12 +825,20 @@ BST_TEST_CASE(testConstraints) BST_REQUIRE(nmos::constraints_validation(1000, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); BST_REQUIRE_EQUAL(nmos::constraints_validation(9, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); BST_REQUIRE_EQUAL(nmos::constraints_validation(1001, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE(nmos::constraints_validation(value_of({ 10, 1000 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 10, 1001 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 10, value::string(U("a")) }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); // property constraints validation BST_REQUIRE(nmos::constraints_validation(50, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); BST_REQUIRE(nmos::constraints_validation(500, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); BST_REQUIRE_EQUAL(nmos::constraints_validation(45, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); BST_REQUIRE_EQUAL(nmos::constraints_validation(505, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); BST_REQUIRE_EQUAL(nmos::constraints_validation(499, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE(nmos::constraints_validation(value_of({ 50, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 49, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 50, 501 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 45, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 50, value::string(U("a")) }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); // datatype constraints validation BST_REQUIRE(nmos::constraints_validation(100, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); BST_REQUIRE(nmos::constraints_validation(250, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); From bc5e0f503bf6a30796df0b04dfe2ed1509657058 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Sat, 18 Nov 2023 13:12:03 +0000 Subject: [PATCH 078/250] Add method parameters constriants validation, and check method deprecation --- .../nmos-cpp-node/node_implementation.cpp | 67 +---- .../nmos/control_protocol_handlers.cpp | 17 +- Development/nmos/control_protocol_handlers.h | 19 +- Development/nmos/control_protocol_methods.cpp | 71 +++--- Development/nmos/control_protocol_methods.h | 26 +- .../nmos/control_protocol_resource.cpp | 12 +- Development/nmos/control_protocol_state.cpp | 28 +-- Development/nmos/control_protocol_state.h | 7 +- Development/nmos/control_protocol_utils.cpp | 50 +++- Development/nmos/control_protocol_utils.h | 22 +- Development/nmos/control_protocol_ws_api.cpp | 31 ++- .../nmos/test/control_protocol_test.cpp | 234 +++++++++--------- 12 files changed, 291 insertions(+), 293 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 1d439f423..cd27d1f04 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1006,81 +1006,34 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr nmos::experimental::make_control_class_property(U("Example object sequence property"), { 3, 13 }, object_sequence, U("ExampleDataType"), false, false, true) }; - auto example_method_with_no_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + auto example_method_with_no_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, const web::json::value& nc_method_descriptor, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... slog::log(gate, SLOG_FLF) << "Executing the example method with no arguments"; - return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::ok }); + return nmos::make_control_protocol_message_response(handle, { nmos::fields::nc::is_deprecated(nc_method_descriptor) ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); }; - auto example_method_with_simple_args = [enum_arg, string_arg, number_arg, boolean_arg, make_string_example_argument_constraints, make_number_example_argument_constraints](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + auto example_method_with_simple_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - using web::json::value; + // and the method parameters constriants has already been validated by the outter function slog::log(gate, SLOG_FLF) << "Executing the example method with simple arguments: " << arguments.serialize(); - // example to do method arguments constraints validation - if (!nmos::constraints_validation(arguments.at(enum_arg), value::null(), value::null(), { nmos::details::get_datatype_descriptor(value::string(U("ExampleEnum")), get_control_protocol_datatype), get_control_protocol_datatype })) - { - slog::log(gate, SLOG_FLF) << "invalid enum_arg: " << arguments.at(enum_arg).serialize(); - return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); - } - if (!nmos::constraints_validation(arguments.at(string_arg), make_string_example_argument_constraints(), value::null(), {nmos::details::get_datatype_descriptor(value::string(U("NcString")), get_control_protocol_datatype), get_control_protocol_datatype})) - { - slog::log(gate, SLOG_FLF) << "invalid string_arg: " << arguments.at(string_arg).serialize(); - return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); - } - if (!nmos::constraints_validation(arguments.at(number_arg), make_number_example_argument_constraints(), value::null(), { nmos::details::get_datatype_descriptor(value::string(U("NcUint64")), get_control_protocol_datatype), get_control_protocol_datatype })) - { - slog::log(gate, SLOG_FLF) << "invalid number_arg: " << arguments.at(number_arg).serialize(); - return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); - } - if (!nmos::constraints_validation(arguments.at(boolean_arg), value::null(), value::null(), { nmos::details::get_datatype_descriptor(value::string(U("NcBoolean")), get_control_protocol_datatype), get_control_protocol_datatype })) - { - slog::log(gate, SLOG_FLF) << "invalid boolean_arg: " << arguments.at(boolean_arg).serialize(); - return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); - } - - return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::ok }); + return nmos::make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); }; - auto example_method_with_object_args = [obj_arg, enum_arg, string_arg, number_arg, boolean_arg, make_string_example_argument_constraints, make_number_example_argument_constraints](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + auto example_method_with_object_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + // and the method parameters constriants has already been validated by the outter function - using web::json::value; - - slog::log(gate, SLOG_FLF) << "Executing the example method with object argument: " << obj_arg(arguments).serialize(); - - // example to do method arguments constraints validation - const auto& obj_arg_ = obj_arg(arguments); - if (!nmos::constraints_validation(obj_arg_.at(enum_arg), value::null(), value::null(), { nmos::details::get_datatype_descriptor(value::string(U("ExampleEnum")), get_control_protocol_datatype), get_control_protocol_datatype })) - { - slog::log(gate, SLOG_FLF) << "invalid enum_arg: " << obj_arg_.at(enum_arg).serialize(); - return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); - } - if (!nmos::constraints_validation(obj_arg_.at(string_arg), make_string_example_argument_constraints(), value::null(), { nmos::details::get_datatype_descriptor(value::string(U("NcString")), get_control_protocol_datatype), get_control_protocol_datatype })) - { - slog::log(gate, SLOG_FLF) << "invalid string_arg: " << obj_arg_.at(string_arg).serialize(); - return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); - } - if (!nmos::constraints_validation(obj_arg_.at(number_arg), make_number_example_argument_constraints(), value::null(), { nmos::details::get_datatype_descriptor(value::string(U("NcUint64")), get_control_protocol_datatype), get_control_protocol_datatype })) - { - slog::log(gate, SLOG_FLF) << "invalid number_arg: " << obj_arg_.at(number_arg).serialize(); - return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); - } - if (!nmos::constraints_validation(obj_arg_.at(boolean_arg), value::null(), value::null(), { nmos::details::get_datatype_descriptor(value::string(U("NcBoolean")), get_control_protocol_datatype), get_control_protocol_datatype })) - { - slog::log(gate, SLOG_FLF) << "invalid boolean_arg: " << obj_arg_.at(boolean_arg).serialize(); - return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); - } + slog::log(gate, SLOG_FLF) << "Executing the example method with object argument: " << arguments.serialize(); - return nmos::make_control_protocol_message_response(handle, { nmos::nc_method_status::ok }); + return nmos::make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); }; // Example control class methods - std::vector> example_control_methods = + std::vector example_control_methods = { { nmos::experimental::make_control_class_method(U("Example method with no arguments"), { 3, 1 }, U("MethodNoArgs"), U("NcMethodResult"), {}, false), example_method_with_no_args }, { nmos::experimental::make_control_class_method(U("Example method with simple arguments"), { 3, 2 }, U("MethodSimpleArgs"), U("NcMethodResult"), diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index e650496aa..f4e0129dd 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -51,18 +51,21 @@ namespace nmos while (!class_id.empty()) { const auto& control_class = get_control_protocol_class(class_id); - auto& methods = control_class.method_handlers; - auto method_found = methods.find(method_id); + auto& methods = control_class.methods; + auto method_found = std::find_if(methods.begin(), methods.end(), [&method_id](const experimental::method& method) + { + return method_id == details::parse_nc_method_id(nmos::fields::nc::id(method.first)); + }); if (methods.end() != method_found) { - return method_found->second; + return *method_found; } class_id.pop_back(); } - return experimental::method_handler(nullptr); + return experimental::method(); }; } @@ -81,9 +84,9 @@ namespace nmos // hmm, maybe updating connectionStatusMessage, payloadStatus, and payloadStatusMessage too const auto propertry_changed_event = make_propertry_changed_event(nmos::fields::nc::oid(found->data), - { - { nc_receiver_monitor_connection_status_property_id, nc_property_change_type::type::value_changed, val } - }); + { + { nc_receiver_monitor_connection_status_property_id, nc_property_change_type::type::value_changed, val } + }); modify_control_protocol_resource(resources, found->id, [&](nmos::resource& resource) { diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index a21277612..580a7a2ca 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -34,19 +34,16 @@ namespace nmos namespace experimental { - // method handler defnition - typedef std::function method_handler; - // methods defnition - typedef std::map methods; // method_id vs method handler - } + // method handler definition + typedef std::function method_handler; - // callback to retrieve all the method handlers - // this callback should not throw exceptions - typedef std::function()> get_control_protocol_methods_handler; + // method definition (NcMethodDescriptor vs method handler) + typedef std::pair method; + } - // callback to retrieve a specific method handler + // callback to retrieve a specific method // this callback should not throw exceptions - typedef std::function get_control_protocol_method_handler; + typedef std::function get_control_protocol_method_handler; // construct callback to retrieve a specific control protocol class get_control_protocol_class_handler make_get_control_protocol_class_handler(experimental::control_protocol_state& control_protocol_state); @@ -54,7 +51,7 @@ namespace nmos // construct callback to retrieve a specific datatype get_control_protocol_datatype_handler make_get_control_protocol_datatype_handler(experimental::control_protocol_state& control_protocol_state); - // construct callback to retrieve a specific method handler + // construct callback to retrieve a specific method get_control_protocol_method_handler make_get_control_protocol_method_handler(experimental::control_protocol_state& control_protocol_state); // a control_protocol_connection_activation_handler is a notification that the active parameters for the specified (IS-05) sender/connection_sender or receiver/connection_receiver have changed diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index d3e4e8196..ae29aa632 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -14,7 +14,7 @@ namespace nmos { // NcObject methods implementation // Get property value - web::json::value get(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value get(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -26,7 +26,7 @@ namespace nmos const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); if (!property.is_null()) { - return make_control_protocol_message_response(handle, { nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(property))); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(property))); } // unknown property @@ -36,7 +36,7 @@ namespace nmos } // Set property value - web::json::value set(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + web::json::value set(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -62,10 +62,10 @@ namespace nmos return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); } - // do constraints validation + // do property constraints validation if (!val.is_null()) { - if (!nmos::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) + if (!nmos::details::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) { return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); } @@ -78,7 +78,7 @@ namespace nmos }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::value_changed, val } })); - return make_control_protocol_message_response(handle, { nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); } // unknown property @@ -88,7 +88,7 @@ namespace nmos } // Get sequence item - web::json::value get_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value get_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -113,7 +113,7 @@ namespace nmos if (data.as_array().size() > (size_t)index) { - return make_control_protocol_message_response(handle, { nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, data.at(index)); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, data.at(index)); } // out of bound @@ -129,7 +129,7 @@ namespace nmos } // Set sequence item - web::json::value set_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + web::json::value set_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -161,8 +161,8 @@ namespace nmos if (data.as_array().size() > (size_t)index) { - // do constraints validation - if (!nmos::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) + // do property constraints validation + if (!nmos::details::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) { return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); } @@ -174,7 +174,7 @@ namespace nmos }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::sequence_item_changed, val, nc_id(index) } })); - return make_control_protocol_message_response(handle, { nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); } // out of bound @@ -190,7 +190,7 @@ namespace nmos } // Add item to sequence - web::json::value add_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + web::json::value add_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -223,8 +223,8 @@ namespace nmos const nc_id sequence_item_index = data.is_null() ? 0 : nc_id(data.as_array().size()); - // do constraints validation - if (!nmos::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) + // do property constraints validation + if (!nmos::details::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) { return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); } @@ -238,7 +238,7 @@ namespace nmos }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::sequence_item_added, val, sequence_item_index } })); - return make_control_protocol_message_response(handle, { nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, sequence_item_index); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, sequence_item_index); } // unknown property @@ -248,7 +248,7 @@ namespace nmos } // Delete sequence item - web::json::value remove_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value remove_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -280,7 +280,7 @@ namespace nmos }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_removed, nc_id(index) } })); - return make_control_protocol_message_response(handle, { nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); } // out of bound @@ -296,7 +296,7 @@ namespace nmos } // Get sequence length - web::json::value get_sequence_length(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value get_sequence_length(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -326,7 +326,7 @@ namespace nmos if (data.is_null()) { // null - return make_control_protocol_message_response(handle, { nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, value::null()); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, value::null()); } } else @@ -340,7 +340,7 @@ namespace nmos return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); } } - return make_control_protocol_message_response(handle, { nc_method_status::ok }, uint32_t(data.as_array().size())); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, uint32_t(data.as_array().size())); } // unknown property @@ -351,7 +351,7 @@ namespace nmos // NcBlock methods implementation // Gets descriptors of members of the block - web::json::value get_member_descriptors(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value get_member_descriptors(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -364,11 +364,11 @@ namespace nmos auto descriptors = value::array(); nmos::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); - return make_control_protocol_message_response(handle, { nc_method_status::ok }, descriptors); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); } // Finds member(s) by path - web::json::value find_members_by_path(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value find_members_by_path(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -422,11 +422,11 @@ namespace nmos } web::json::push_back(descriptors, descriptor); - return make_control_protocol_message_response(handle, { nc_method_status::ok }, descriptors); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); } // Finds members with given role name or fragment - web::json::value find_members_by_role(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value find_members_by_role(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -448,11 +448,11 @@ namespace nmos auto descriptors = value::array(); nmos::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); - return make_control_protocol_message_response(handle, { nc_method_status::ok }, descriptors); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); } // Finds members with given class id - web::json::value find_members_by_class_id(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value find_members_by_class_id(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -475,12 +475,12 @@ namespace nmos auto descriptors = value::array(); nmos::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); - return make_control_protocol_message_response(handle, { nc_method_status::ok }, descriptors); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); } // NcClassManager methods implementation // Get a single class descriptor - web::json::value get_control_class(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value get_control_class(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) { using web::json::value; @@ -504,7 +504,8 @@ namespace nmos auto& name = control_class.name; auto& fixed_role = control_class.fixed_role; auto properties = control_class.properties; - auto methods = control_class.methods; + auto methods = value::array(); + for (const auto& method : control_class.methods) { web::json::push_back(methods, method.first); } auto events = control_class.events; if (include_inherited) @@ -517,7 +518,7 @@ namespace nmos const auto& inherited_control_class = get_control_protocol_class(inherited_class_id); { for (const auto& property : inherited_control_class.properties.as_array()) { web::json::push_back(properties, property); } - for (const auto& method : inherited_control_class.methods.as_array()) { web::json::push_back(methods, method); } + for (const auto& method : inherited_control_class.methods) { web::json::push_back(methods, method.first); } for (const auto& event : inherited_control_class.events.as_array()) { web::json::push_back(events, event); } } inherited_class_id.pop_back(); @@ -527,14 +528,14 @@ namespace nmos ? details::make_nc_class_descriptor(description, class_id, name, properties, methods, events) : details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), properties, methods, events); - return make_control_protocol_message_response(handle, { nc_method_status::ok }, descriptor); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptor); } return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("classId not found")); } // Get a single datatype descriptor - web::json::value get_datatype(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + web::json::value get_datatype(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -586,7 +587,7 @@ namespace nmos } } - return make_control_protocol_message_response(handle, { nc_method_status::ok }, descriptor); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptor); } return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("name not found")); diff --git a/Development/nmos/control_protocol_methods.h b/Development/nmos/control_protocol_methods.h index 3e7d136e5..d0eec3ca7 100644 --- a/Development/nmos/control_protocol_methods.h +++ b/Development/nmos/control_protocol_methods.h @@ -15,35 +15,35 @@ namespace nmos { // NcObject methods implementation // Get property value - web::json::value get(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value get(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); // Set property value - web::json::value set(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value set(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); // Get sequence item - web::json::value get_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value get_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); // Set sequence item - web::json::value set_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value set_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); // Add item to sequence - web::json::value add_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value add_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); // Delete sequence item - web::json::value remove_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value remove_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); // Get sequence length - web::json::value get_sequence_length(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value get_sequence_length(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); // NcBlock methods implementation // Get descriptors of members of the block - web::json::value get_member_descriptors(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value get_member_descriptors(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate); // Finds member(s) by path - web::json::value find_members_by_path(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value find_members_by_path(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate); // Finds members with given role name or fragment - web::json::value find_members_by_role(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value find_members_by_role(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate); // Finds members with given class id - web::json::value find_members_by_class_id(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value find_members_by_class_id(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate); // NcClassManager methods implementation // Get a single class descriptor - web::json::value get_control_class(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value get_control_class(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); // Get a single datatype descriptor - web::json::value get_datatype(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, get_control_protocol_class_handler, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate); + web::json::value get_datatype(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate); } } diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 61ab4b3ec..248b3aba5 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -65,7 +65,7 @@ namespace nmos { return make_nc_element_id(id); } - nc_event_id parse_nc_method_id(const web::json::value& id) + nc_method_id parse_nc_method_id(const web::json::value& id) { return parse_nc_element_id(id); } @@ -75,7 +75,7 @@ namespace nmos { return make_nc_element_id(id); } - nc_event_id parse_nc_property_id(const web::json::value& id) + nc_property_id parse_nc_property_id(const web::json::value& id) { return parse_nc_element_id(id); } @@ -781,9 +781,13 @@ namespace nmos for (const auto& control_class : control_protocol_state.control_classes) { auto& ctl_class = control_class.second; + + auto methods = value::array(); + for (const auto& method : ctl_class.methods) { web::json::push_back(methods, method.first); } + const auto class_description = ctl_class.fixed_role.is_null() - ? make_nc_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.properties, ctl_class.methods, ctl_class.events) - : make_nc_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.fixed_role.as_string(), ctl_class.properties, ctl_class.methods, ctl_class.events); + ? make_nc_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.properties, methods, ctl_class.events) + : make_nc_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.fixed_role.as_string(), ctl_class.properties, methods, ctl_class.events); web::json::push_back(control_classes, class_description); } diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 60661b966..3cc3af38a 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -12,25 +12,18 @@ namespace nmos // create control class // where // properties: vector of NcPropertyDescriptor can be constructed using make_control_class_property - // methods: vector of NcMethodDescriptor can be constructed using make_nc_method_descriptor and the assoicated method handler + // methods: vector of NcMethodDescriptor vs assoicated method handler where NcMethodDescriptor can be constructed using make_nc_method_descriptor // events: vector of NcEventDescriptor can be constructed using make_nc_event_descriptor - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const std::vector& properties_, const std::vector>& methods_, const std::vector& events_) + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const std::vector& properties_, const std::vector& methods_, const std::vector& events_) { using web::json::value; web::json::value properties = value::array(); for (const auto& property : properties_) { web::json::push_back(properties, property); } - web::json::value methods = value::array(); - nmos::experimental::methods method_handlers; - for (const auto& method : methods_) - { - web::json::push_back(methods, method.first); - method_handlers[nmos::details::parse_nc_method_id(nmos::fields::nc::id(method.first))] = method.second; - } web::json::value events = value::array(); for (const auto& event : events_) { web::json::push_back(events, event); } - return { description, class_id, name, fixed_role, properties, methods, events, method_handlers }; + return { description, class_id, name, fixed_role, properties, methods_, events }; } } // create control class with fixed role @@ -38,7 +31,7 @@ namespace nmos // properties: vector of NcPropertyDescriptor which can be constructed using make_control_class_property // methods: vector of NcMethodDescriptor which can be constructed using make_nc_method_descriptor and the assoicated method handler // events: vector of NcEventDescriptor can be constructed using make_nc_event_descriptor - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const std::vector& properties, const std::vector>& methods, const std::vector& events) + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const std::vector& properties, const std::vector& methods, const std::vector& events) { using web::json::value; @@ -49,7 +42,7 @@ namespace nmos // properties: vector of NcPropertyDescriptor which can be constructed using make_control_class_property // methods: vector of NcMethodDescriptor which can be constructed using make_nc_method_descriptor and the assoicated method handler // events: vector of NcEventDescriptor can be constructed using make_nc_event_descriptor - control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const std::vector& properties, const std::vector>& methods, const std::vector& events) + control_class make_control_class(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const std::vector& properties, const std::vector& methods, const std::vector& events) { using web::json::value; @@ -98,15 +91,16 @@ namespace nmos return std::vector{}; }; - auto to_methods_vector = [](const web::json::value& method_data_array, const nmos::experimental::methods& method_handlers) + auto to_methods_vector = [](const web::json::value& nc_method_descriptors, const std::map& method_handlers) { - std::vector> methods; + // NcMethodDescriptor vs method_handler + std::vector methods; - if (!method_data_array.is_null()) + if (!nc_method_descriptors.is_null()) { - for (auto& method_data : method_data_array.as_array()) + for (const auto& nc_method_descriptor : nc_method_descriptors.as_array()) { - methods.push_back({ method_data, method_handlers.at(nmos::details::parse_nc_method_id(nmos::fields::nc::id(method_data))) }); + methods.push_back({ nc_method_descriptor, method_handlers.at(nmos::details::parse_nc_method_id(nmos::fields::nc::id(nc_method_descriptor))) }); } } return methods; diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index 54431fca8..a43d625e3 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -21,16 +21,14 @@ namespace nmos web::json::value fixed_role; web::json::value properties = web::json::value::array(); // array of NcPropertyDescriptor - web::json::value methods = web::json::value::array(); // array of NcMethodDescriptor + std::vector methods; // vector of NcMethodDescriptor and method_handler web::json::value events = web::json::value::array(); // array of NcEventDescriptor - nmos::experimental::methods method_handlers; // map of method handlers which are associated to this control_class (class_id), but not including its base class - control_class() : class_id({ 0 }) {} - control_class(utility::string_t description, nmos::nc_class_id class_id, nmos::nc_name name, web::json::value fixed_role, web::json::value properties, web::json::value methods, web::json::value events, nmos::experimental::methods method_handlers) + control_class(utility::string_t description, nmos::nc_class_id class_id, nmos::nc_name name, web::json::value fixed_role, web::json::value properties, std::vector methods, web::json::value events) : description(std::move(description)) , class_id(std::move(class_id)) , name(std::move(name)) @@ -38,7 +36,6 @@ namespace nmos , properties(std::move(properties)) , methods(std::move(methods)) , events(std::move(events)) - , method_handlers(std::move(method_handlers)) {} }; diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 07260eb60..2923b189a 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -151,6 +151,9 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html bool datatype_constraints_validation(const web::json::value& data, const datatype_constraints_validation_parameters& params) { + // no constraints validation required + if (params.datatype_descriptor.is_null()) { return true; } + const auto& datatype_type = nmos::fields::nc::type(params.datatype_descriptor); // do NcDatatypeDescriptorPrimitive constraints validation @@ -287,6 +290,29 @@ namespace nmos // unsupport datatype_type, no validation is required return true; } + + // multiple levels of constraints validation + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + bool constraints_validation(const web::json::value& data, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) + { + // do level 2 runtime property constraints validation + if (!runtime_property_constraints.is_null()) { return details::constraints_validation(data, runtime_property_constraints); } + + // do level 1 property constraints validation + if (!property_constraints.is_null()) { return details::constraints_validation(data, property_constraints); } + + // do level 0 datatype constraints validation + return details::datatype_constraints_validation(data, params); + } + + // method parameter constraints validation + bool method_parameter_constraints_validation(const web::json::value& data, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) + { + using web::json::value; + + // do level 1 property constraints & level 0 datatype constraints validation + return constraints_validation(data, value::null(), property_constraints, params); + } } // is the given class_id a NcBlock @@ -562,17 +588,19 @@ namespace nmos }); } - // multiple levels of constraints validation - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - bool constraints_validation(const web::json::value& data, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) + // method parameters constraints validation + bool method_parameters_contraints_validation(const web::json::value& arguments, const web::json::value& nc_method_descriptor, get_control_protocol_datatype_handler get_control_protocol_datatype) { - // do level 2 runtime property constraints validation - if (!runtime_property_constraints.is_null()) { return details::constraints_validation(data, runtime_property_constraints); } - - // do level 1 property constraints validation - if (!property_constraints.is_null()) { return details::constraints_validation(data, property_constraints); } - - // do level 0 datatype constraints validation - return details::datatype_constraints_validation(data, params); + for (const auto& param : nmos::fields::nc::parameters(nc_method_descriptor)) + { + const auto& name = nmos::fields::nc::name(param); + const auto& constraints = nmos::fields::nc::constraints(param); + const auto& type_name = param.at(nmos::fields::nc::type_name); + if (!details::method_parameter_constraints_validation(arguments.at(name), constraints, { nmos::details::get_datatype_descriptor(type_name, get_control_protocol_datatype), get_control_protocol_datatype })) + { + return false; + } + } + return true; } } diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index 1718e01cc..34ad7c593 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -18,6 +18,18 @@ namespace nmos // get the datatype property constraints of a given type_name web::json::value get_datatype_constraints(const web::json::value& type_name, get_control_protocol_datatype_handler get_control_protocol_datatype); + + struct datatype_constraints_validation_parameters + { + web::json::value datatype_descriptor; + get_control_protocol_datatype_handler get_control_protocol_datatype; + }; + // multiple levels of constraints validation + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + bool constraints_validation(const web::json::value& value, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params); + + // method parameter constraints validation + bool method_parameter_constraints_validation(const web::json::value& data, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params); } // is the given class_id a NcBlock @@ -60,14 +72,8 @@ namespace nmos // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id resources::const_iterator find_control_protocol_resource(resources& resources, type type, const id& id); - struct datatype_constraints_validation_parameters - { - web::json::value datatype_descriptor; - get_control_protocol_datatype_handler get_control_protocol_datatype; - }; - // multiple levels of constraints validation - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - bool constraints_validation(const web::json::value& value, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params); + // method parameters constraints validation + bool method_parameters_contraints_validation(const web::json::value& arguments, const web::json::value& nc_method_descriptor, get_control_protocol_datatype_handler get_control_protocol_datatype); } #endif diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index beb54823d..06620a1fe 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -6,6 +6,7 @@ #include "nmos/api_utils.h" #include "nmos/control_protocol_resources.h" #include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_utils.h" #include "nmos/is12_versions.h" #include "nmos/json_schema.h" #include "nmos/model.h" @@ -245,6 +246,8 @@ namespace nmos // get arguments const auto& arguments = nmos::fields::nc::arguments(cmd); + value response; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != resource) { @@ -252,17 +255,28 @@ namespace nmos // find the relevent method handler to execute auto method = get_control_protocol_method(class_id, method_id); - if (method) + if (method.second) { - // execute the relevant method handler, then accumulating up their response to reponses - web::json::push_back(responses, method(resources, resource, handle, arguments, get_control_protocol_class, get_control_protocol_datatype, gate)); + // do method arguments constraints validation + if (method_parameters_contraints_validation(arguments, method.first, get_control_protocol_datatype)) + { + // execute the relevant method handler, then accumulating up their response to reponses + response = method.second(resources, resource, handle, arguments, nmos::fields::nc::is_deprecated(method.first), get_control_protocol_class, get_control_protocol_datatype, gate); + } + else + { + // invalid arguments + slog::log(gate, SLOG_FLF) << "invalid argument: " << arguments.serialize(); + response = make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); + } } else { + // unknown methodId utility::stringstream_t ss; - ss << U("unsupported method id: ") << nmos::fields::nc::method_id(cmd).serialize(); - web::json::push_back(responses, - make_control_protocol_error_response(handle, { nc_method_status::method_not_implemented }, ss.str())); + ss << U("unsupported method_id: ") << nmos::fields::nc::method_id(cmd).serialize() + << U(" for control class class_id: ") << resource->data.at(nmos::fields::nc::class_id).serialize(); + response = make_control_protocol_error_response(handle, { nc_method_status::method_not_implemented }, ss.str()); } } else @@ -270,9 +284,10 @@ namespace nmos // resource not found for the given oid utility::stringstream_t ss; ss << U("unknown oid: ") << oid; - web::json::push_back(responses, - make_control_protocol_error_response(handle, { nc_method_status::bad_oid }, ss.str())); + response = make_control_protocol_error_response(handle, { nc_method_status::bad_oid }, ss.str()); } + // accumulating up response + web::json::push_back(responses, response); } // add command_response to the grain ready to transfer to the client in nmos::send_control_protocol_ws_messages_thread diff --git a/Development/nmos/test/control_protocol_test.cpp b/Development/nmos/test/control_protocol_test.cpp index 381433923..36c914809 100644 --- a/Development/nmos/test/control_protocol_test.cpp +++ b/Development/nmos/test/control_protocol_test.cpp @@ -796,121 +796,121 @@ BST_TEST_CASE(testConstraints) // string property constraints validation // runtime property constraints validation - const nmos::datatype_constraints_validation_parameters with_constraints_string_constraints_validation_params{ with_constraints_string_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE(nmos::constraints_validation(value::string(U("1234567890")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("12345678901")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("123456789A")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); - BST_REQUIRE(nmos::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("1234567890")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("12345678901")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ value::string(U("1234567890")), 1 }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); + const nmos::details::datatype_constraints_validation_parameters with_constraints_string_constraints_validation_params{ with_constraints_string_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE(nmos::details::constraints_validation(value::string(U("1234567890")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value::string(U("12345678901")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value::string(U("123456789A")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); + BST_REQUIRE(nmos::details::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("1234567890")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("12345678901")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ value::string(U("1234567890")), 1 }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); // property constraints validation - BST_REQUIRE(nmos::constraints_validation(value::string(U("abcde")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("abcdef")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("abcd1")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); - BST_REQUIRE(nmos::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcde")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcdef")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ value::string(U("abcde")), 1 }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); + BST_REQUIRE(nmos::details::constraints_validation(value::string(U("abcde")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value::string(U("abcdef")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value::string(U("abcd1")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); + BST_REQUIRE(nmos::details::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcde")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcdef")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ value::string(U("abcde")), 1 }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); // datatype constraints validation - BST_REQUIRE(nmos::constraints_validation(value::string(U("1a")), value::null(), value::null(), with_constraints_string_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("1a2")), value::null(), value::null(), with_constraints_string_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("1*")), value::null(), value::null(), with_constraints_string_constraints_validation_params), false); - const nmos::datatype_constraints_validation_parameters no_constraints_string_constraints_validation_params{ no_constraints_string_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE(nmos::constraints_validation(value::string(U("1234567890-abcde-!\"£$%^&*()_+=")), value::null(), value::null(), no_constraints_string_constraints_validation_params)); + BST_REQUIRE(nmos::details::constraints_validation(value::string(U("1a")), value::null(), value::null(), with_constraints_string_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value::string(U("1a2")), value::null(), value::null(), with_constraints_string_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value::string(U("1*")), value::null(), value::null(), with_constraints_string_constraints_validation_params), false); + const nmos::details::datatype_constraints_validation_parameters no_constraints_string_constraints_validation_params{ no_constraints_string_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE(nmos::details::constraints_validation(value::string(U("1234567890-abcde-!\"£$%^&*()_+=")), value::null(), value::null(), no_constraints_string_constraints_validation_params)); // number property constraints validation // runtime property constraints validation - const nmos::datatype_constraints_validation_parameters with_constraints_int32_constraints_validation_params{ with_constraints_int32_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE(nmos::constraints_validation(10, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE(nmos::constraints_validation(1000, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(9, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(1001, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE(nmos::constraints_validation(value_of({ 10, 1000 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 10, 1001 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 10, value::string(U("a")) }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + const nmos::details::datatype_constraints_validation_parameters with_constraints_int32_constraints_validation_params{ with_constraints_int32_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE(nmos::details::constraints_validation(10, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE(nmos::details::constraints_validation(1000, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(9, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(1001, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE(nmos::details::constraints_validation(value_of({ 10, 1000 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 10, 1001 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 10, value::string(U("a")) }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); // property constraints validation - BST_REQUIRE(nmos::constraints_validation(50, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE(nmos::constraints_validation(500, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(45, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(505, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(499, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE(nmos::constraints_validation(value_of({ 50, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 49, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 50, 501 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 45, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 50, value::string(U("a")) }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE(nmos::details::constraints_validation(50, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE(nmos::details::constraints_validation(500, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(45, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(505, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(499, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE(nmos::details::constraints_validation(value_of({ 50, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 49, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 50, 501 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 45, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 50, value::string(U("a")) }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); // datatype constraints validation - BST_REQUIRE(nmos::constraints_validation(100, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); - BST_REQUIRE(nmos::constraints_validation(250, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(90, value::null(), value::null(), with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(260, value::null(), value::null(), with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(99, value::null(), value::null(), with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE(nmos::details::constraints_validation(100, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); + BST_REQUIRE(nmos::details::constraints_validation(250, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(90, value::null(), value::null(), with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(260, value::null(), value::null(), with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(99, value::null(), value::null(), with_constraints_int32_constraints_validation_params), false); // int16 datatype constraints validation - const nmos::datatype_constraints_validation_parameters no_constraints_int16_constraints_validation_params{ no_constraints_int16_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), false); - BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); - BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); + const nmos::details::datatype_constraints_validation_parameters no_constraints_int16_constraints_validation_params{ no_constraints_int16_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), false); + BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); + BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); // int32 datatype constraints validation - const nmos::datatype_constraints_validation_parameters no_constraints_int32_constraints_validation_params{ no_constraints_int32_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), false); - BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); - BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); + const nmos::details::datatype_constraints_validation_parameters no_constraints_int32_constraints_validation_params{ no_constraints_int32_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), false); + BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); + BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); // int64 datatype constraints validation - const nmos::datatype_constraints_validation_parameters no_constraints_int64_constraints_validation_params{ no_constraints_int64_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), false); - BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); - BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); + const nmos::details::datatype_constraints_validation_parameters no_constraints_int64_constraints_validation_params{ no_constraints_int64_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), false); + BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); + BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); // uint16 datatype constraints validation - const nmos::datatype_constraints_validation_parameters no_constraints_uint16_constraints_validation_params{ no_constraints_uint16_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::constraints_validation(-1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), false); - BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); - BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); + const nmos::details::datatype_constraints_validation_parameters no_constraints_uint16_constraints_validation_params{ no_constraints_uint16_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), false); + BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); + BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); // uint32 datatype constraints validation - const nmos::datatype_constraints_validation_parameters no_constraints_uint32_constraints_validation_params{ no_constraints_uint32_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::constraints_validation(-1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), false); - BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); - BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); + const nmos::details::datatype_constraints_validation_parameters no_constraints_uint32_constraints_validation_params{ no_constraints_uint32_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), false); + BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); + BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); // uint64 datatype constraints validation - const nmos::datatype_constraints_validation_parameters no_constraints_uint64_constraints_validation_params{ no_constraints_uint64_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::constraints_validation(-1, value::null(), value::null(), no_constraints_uint64_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), false); - BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); - BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); + const nmos::details::datatype_constraints_validation_parameters no_constraints_uint64_constraints_validation_params{ no_constraints_uint64_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint64_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), false); + BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); + BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); // float32 datatype constraints validation - const nmos::datatype_constraints_validation_parameters no_constraints_float32_constraints_validation_params{ no_constraints_float32_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), false); - BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); - BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); + const nmos::details::datatype_constraints_validation_parameters no_constraints_float32_constraints_validation_params{ no_constraints_float32_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), false); + BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); + BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); // float64 datatype constraints validation - const nmos::datatype_constraints_validation_parameters no_constraints_float64_constraints_validation_params{ no_constraints_float64_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::constraints_validation(1000, value::null(), value::null(), no_constraints_float64_constraints_validation_params), false); - BST_REQUIRE(nmos::constraints_validation(1000.0, value::null(), value::null(), no_constraints_float64_constraints_validation_params)); - BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); - BST_REQUIRE(nmos::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); + const nmos::details::datatype_constraints_validation_parameters no_constraints_float64_constraints_validation_params{ no_constraints_float64_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(1000, value::null(), value::null(), no_constraints_float64_constraints_validation_params), false); + BST_REQUIRE(nmos::details::constraints_validation(1000.0, value::null(), value::null(), no_constraints_float64_constraints_validation_params)); + BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); + BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); // enum property datatype constraints validation - const nmos::datatype_constraints_validation_parameters enum_constraints_validation_params{ enum_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE(nmos::constraints_validation(enum_value::foo, value::null(), value::null(), enum_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(4, value::null(), value::null(), enum_constraints_validation_params), false); + const nmos::details::datatype_constraints_validation_parameters enum_constraints_validation_params{ enum_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE(nmos::details::constraints_validation(enum_value::foo, value::null(), value::null(), enum_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(4, value::null(), value::null(), enum_constraints_validation_params), false); // invalid data vs primitive datatype constraints - const nmos::datatype_constraints_validation_parameters no_constraints_string_seq_constraints_validation_params{ no_constraints_string_seq_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE(nmos::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); - BST_REQUIRE(nmos::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")), value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(1, value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), false); - const nmos::datatype_constraints_validation_parameters no_constraints_int32_seq_constraints_validation_params{ no_constraints_int32_seq_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), false); - BST_REQUIRE(nmos::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); - BST_REQUIRE(nmos::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value::string(U("1234567890-abcde-!\"£$%^&*()_+=")), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_string_constraints_validation_params), false); + const nmos::details::datatype_constraints_validation_parameters no_constraints_string_seq_constraints_validation_params{ no_constraints_string_seq_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); + BST_REQUIRE(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")), value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(1, value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), false); + const nmos::details::datatype_constraints_validation_parameters no_constraints_int32_seq_constraints_validation_params{ no_constraints_int32_seq_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), false); + BST_REQUIRE(nmos::details::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); + BST_REQUIRE(nmos::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value::string(U("1234567890-abcde-!\"£$%^&*()_+=")), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_string_constraints_validation_params), false); // struct property datatype constraints validation const auto good_struct = value_of({ @@ -1441,24 +1441,24 @@ BST_TEST_CASE(testConstraints) }) }) } }); - const nmos::datatype_constraints_validation_parameters struct_constraints_validation_params{ struct_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE(nmos::constraints_validation(good_struct, value::null(), value::null(), struct_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct1, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_1, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_2, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_3, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_4, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_5, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_5_1, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_5_2, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_5_3, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_6, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_6_1, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_6_2, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_6_3, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_7, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_7_1, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_7_2, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::constraints_validation(bad_struct2_7_3, value::null(), value::null(), struct_constraints_validation_params), false); + const nmos::details::datatype_constraints_validation_parameters struct_constraints_validation_params{ struct_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; + BST_REQUIRE(nmos::details::constraints_validation(good_struct, value::null(), value::null(), struct_constraints_validation_params)); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct1, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_1, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_2, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_3, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_4, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_5, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_5_1, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_5_2, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_5_3, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_6, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_6_1, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_6_2, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_6_3, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_7, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_7_1, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_7_2, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_7_3, value::null(), value::null(), struct_constraints_validation_params), false); } From 65e8ebbab15f41a55767e38f02d074c37849adf5 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Mon, 20 Nov 2023 12:28:39 +0000 Subject: [PATCH 079/250] Add comments --- Development/nmos/control_protocol_ws_api.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 06620a1fe..3660a6d06 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -228,6 +228,7 @@ namespace nmos const auto msg_type = nmos::fields::nc::message_type(message); switch (msg_type) { + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#command-message-type case ncp_message_type::command: { // validate command-message @@ -299,6 +300,7 @@ namespace nmos }); } break; + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#subscription-message-type case ncp_message_type::subscription: { // validate subscription-message From 092519e58e042cd22e155404288a1a1ef54384d1 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Mon, 20 Nov 2023 12:39:05 +0000 Subject: [PATCH 080/250] No arguments object to those methods which do not require any arguments --- Development/nmos/control_protocol_utils.cpp | 5 +++++ Development/nmos/json_fields.h | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 2923b189a..529ced833 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -596,6 +596,11 @@ namespace nmos const auto& name = nmos::fields::nc::name(param); const auto& constraints = nmos::fields::nc::constraints(param); const auto& type_name = param.at(nmos::fields::nc::type_name); + if (arguments.is_null() || !arguments.has_field(name)) + { + // missing argument parameter + return false; + } if (!details::method_parameter_constraints_validation(arguments.at(name), constraints, { nmos::details::get_datatype_descriptor(type_name, get_control_protocol_datatype), get_control_protocol_datatype })) { return false; diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index 12bfa9bbf..aab74e70d 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -238,10 +238,10 @@ namespace nmos // for control_protocol_ws_api commands const web::json::field_as_array commands{ U("commands") }; - const web::json::field_as_array subscriptions{ U("subscriptions")}; + const web::json::field_as_array subscriptions{ U("subscriptions") }; const web::json::field_as_integer oid{ U("oid") }; const web::json::field_as_value method_id{ U("methodId") }; - const web::json::field_as_value arguments{ U("arguments") }; + const web::json::field_as_value_or arguments{ U("arguments"), {} }; const web::json::field_as_value id{ U("id") }; const web::json::field_as_integer level{ U("level") }; const web::json::field_as_integer index{ U("index") }; From 14aa16f00af2fea6425fa97ca85ff27825d9d31a Mon Sep 17 00:00:00 2001 From: lo-simon Date: Mon, 20 Nov 2023 13:49:21 +0000 Subject: [PATCH 081/250] Add deprecated property and deprecated method to Example Control Class --- .../nmos-cpp-node/node_implementation.cpp | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index cd27d1f04..035400aac 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -954,6 +954,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const web::json::field_as_number enum_property{ U("enumProperty") }; const web::json::field_as_string string_property{ U("stringProperty") }; const web::json::field_as_number number_property{ U("numberProperty") }; + const web::json::field_as_number deprecated_number_property{ U("deprecatedNumberProperty") }; const web::json::field_as_bool boolean_property{ U("booleanProperty") }; const web::json::field_as_value object_property{ U("objectProperty") }; const web::json::field_as_number method_no_args_count{ U("methodNoArgsCount") }; @@ -990,29 +991,30 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // create "Example numeric property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html // use nmos::details::make_nc_parameter_constraints_number to create property constraints nmos::experimental::make_control_class_property(U("Example numeric property"), { 3, 3 }, number_property, U("NcUint64"), false, false, false, false, make_number_example_argument_constraints()), - nmos::experimental::make_control_class_property(U("Example boolean property"), { 3, 4 }, boolean_property, U("NcBoolean")), - nmos::experimental::make_control_class_property(U("Example object property"), { 3, 5 }, object_property, U("ExampleDataType")), - nmos::experimental::make_control_class_property(U("Method no args invoke counter"), { 3, 6 }, method_no_args_count, U("NcUint64"), true), - nmos::experimental::make_control_class_property(U("Method simple args invoke counter"), { 3, 7 }, method_simple_args_count, U("NcUint64"), true), - nmos::experimental::make_control_class_property(U("Method obj arg invoke counter"), { 3, 8 }, method_object_arg_count, U("NcUint64"), true), + nmos::experimental::make_control_class_property(U("Example deprecated numeric property"), { 3, 4 }, deprecated_number_property, U("NcUint64"), false, false, false, true, make_number_example_argument_constraints()), + nmos::experimental::make_control_class_property(U("Example boolean property"), { 3, 5 }, boolean_property, U("NcBoolean")), + nmos::experimental::make_control_class_property(U("Example object property"), { 3, 6 }, object_property, U("ExampleDataType")), + nmos::experimental::make_control_class_property(U("Example method no args invoke counter"), { 3, 7 }, method_no_args_count, U("NcUint64"), true), + nmos::experimental::make_control_class_property(U("Example method simple args invoke counter"), { 3, 8 }, method_simple_args_count, U("NcUint64"), true), + nmos::experimental::make_control_class_property(U("Example method obj arg invoke counter"), { 3, 9 }, method_object_arg_count, U("NcUint64"), true), // create "Example sequence string property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html // use nmos::details::make_nc_parameter_constraints_string to create sequence property constraints - nmos::experimental::make_control_class_property(U("Example string sequence property"), { 3, 9 }, string_sequence, U("NcString"), false, false, true, false, make_string_example_argument_constraints()), - nmos::experimental::make_control_class_property(U("Example boolean sequence property"), { 3, 10 }, boolean_sequence, U("NcBoolean"), false, false, true), - nmos::experimental::make_control_class_property(U("Example enum sequence property"), { 3, 11 }, enum_sequence, U("ExampleEnum"), false, false, true), + nmos::experimental::make_control_class_property(U("Example string sequence property"), { 3, 10 }, string_sequence, U("NcString"), false, false, true, false, make_string_example_argument_constraints()), + nmos::experimental::make_control_class_property(U("Example boolean sequence property"), { 3, 11 }, boolean_sequence, U("NcBoolean"), false, false, true), + nmos::experimental::make_control_class_property(U("Example enum sequence property"), { 3, 12 }, enum_sequence, U("ExampleEnum"), false, false, true), // create "Example sequence numeric property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html // use nmos::details::make_nc_parameter_constraints_number to create sequence property constraints - nmos::experimental::make_control_class_property(U("Example number sequence property"), { 3, 12 }, number_sequence, U("NcUint64"), false, false, true, false, make_number_example_argument_constraints()), - nmos::experimental::make_control_class_property(U("Example object sequence property"), { 3, 13 }, object_sequence, U("ExampleDataType"), false, false, true) + nmos::experimental::make_control_class_property(U("Example number sequence property"), { 3, 13 }, number_sequence, U("NcUint64"), false, false, true, false, make_number_example_argument_constraints()), + nmos::experimental::make_control_class_property(U("Example object sequence property"), { 3, 14 }, object_sequence, U("ExampleDataType"), false, false, true) }; - auto example_method_with_no_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, const web::json::value& nc_method_descriptor, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + auto example_method_with_no_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... slog::log(gate, SLOG_FLF) << "Executing the example method with no arguments"; - return nmos::make_control_protocol_message_response(handle, { nmos::fields::nc::is_deprecated(nc_method_descriptor) ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); + return nmos::make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); }; auto example_method_with_simple_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) { @@ -1036,7 +1038,8 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr std::vector example_control_methods = { { nmos::experimental::make_control_class_method(U("Example method with no arguments"), { 3, 1 }, U("MethodNoArgs"), U("NcMethodResult"), {}, false), example_method_with_no_args }, - { nmos::experimental::make_control_class_method(U("Example method with simple arguments"), { 3, 2 }, U("MethodSimpleArgs"), U("NcMethodResult"), + { nmos::experimental::make_control_class_method(U("Example deprecated method with no arguments"), { 3, 2 }, U("MethodNoArgs"), U("NcMethodResult"), {}, true), example_method_with_no_args }, + { nmos::experimental::make_control_class_method(U("Example method with simple arguments"), { 3, 3 }, U("MethodSimpleArgs"), U("NcMethodResult"), { nmos::details::make_nc_parameter_descriptor(U("Enum example argument"), enum_arg, U("ExampleEnum"), false, false, value::null()), nmos::details::make_nc_parameter_descriptor(U("String example argument"), string_arg, U("NcString"), false, false, make_string_example_argument_constraints()), // e.g. include method property constraints @@ -1045,7 +1048,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr }, false), example_method_with_simple_args }, - { nmos::experimental::make_control_class_method(U("Example method with object argument"), { 3, 3 }, U("MethodObjectArg"), U("NcMethodResult"), + { nmos::experimental::make_control_class_method(U("Example method with object argument"), { 3, 4 }, U("MethodObjectArg"), U("NcMethodResult"), { nmos::details::make_nc_parameter_descriptor(U("Object example argument"), obj_arg, U("ExampleDataType"), false, false, value::null()) }, @@ -1114,6 +1117,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr example_enum enum_property_ = example_enum::Undefined, const utility::string_t& string_property_ = U(""), uint64_t number_property_ = 0, + uint64_t deprecated_number_property_ = 0, bool boolean_property_ = true, const value& object_property_ = value::null(), uint64_t method_no_args_count_ = 0, @@ -1129,6 +1133,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr data[enum_property] = value::number(enum_property_); data[string_property] = value::string(string_property_); data[number_property] = value::number(number_property_); + data[deprecated_number_property] = value::number(deprecated_number_property_); data[boolean_property] = value::boolean(boolean_property_); data[object_property] = object_property_; data[method_no_args_count] = value::number(method_no_args_count_); @@ -1233,6 +1238,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr example_enum::Undefined, U("test"), 3, + 10, false, make_example_datatype(example_enum::Undefined, U("default"), 5, false), 0, From 0bed427b470084c262093d5769c58b672dee2bbf Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 21 Nov 2023 00:33:25 +0000 Subject: [PATCH 082/250] Add logging for contraints validation --- Development/nmos/control_protocol_methods.cpp | 83 +++--- Development/nmos/control_protocol_utils.cpp | 240 ++++++++++-------- Development/nmos/control_protocol_utils.h | 17 +- Development/nmos/control_protocol_ws_api.cpp | 10 +- .../nmos/test/control_protocol_test.cpp | 204 +++++++-------- 5 files changed, 302 insertions(+), 252 deletions(-) diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index ae29aa632..bfde62701 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -62,23 +62,26 @@ namespace nmos return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); } - // do property constraints validation - if (!val.is_null()) + try { - if (!nmos::details::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) + // do property constraints validation + nmos::details::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype }); + + // update property + modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) { - return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); - } - } + resource.data[nmos::fields::nc::name(property)] = val; - // update property - modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) - { - resource.data[nmos::fields::nc::name(property)] = val; + }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::value_changed, val } })); - }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::value_changed, val } })); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); + } + catch (const nmos::control_protocol_exception& e) + { + slog::log(gate, SLOG_FLF) << "Set property: " << property_id.serialize() << " value: " << val.serialize() << " error: " << e.what(); - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); + return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); + } } // unknown property @@ -161,20 +164,26 @@ namespace nmos if (data.as_array().size() > (size_t)index) { - // do property constraints validation - if (!nmos::details::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) + try { - return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); - } + // do property constraints validation + nmos::details::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype }); - // update property - modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) - { - resource.data[nmos::fields::nc::name(property)][index] = val; + // update property + modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) + { + resource.data[nmos::fields::nc::name(property)][index] = val; - }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::sequence_item_changed, val, nc_id(index) } })); + }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::sequence_item_changed, val, nc_id(index) } })); - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); + } + catch (const nmos::control_protocol_exception& e) + { + slog::log(gate, SLOG_FLF) << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize() << " error: " << e.what(); + + return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); + } } // out of bound @@ -223,22 +232,28 @@ namespace nmos const nc_id sequence_item_index = data.is_null() ? 0 : nc_id(data.as_array().size()); - // do property constraints validation - if (!nmos::details::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype })) + try { - return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); - } + // do property constraints validation + nmos::details::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype }); - // update property - modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) - { - auto& sequence = resource.data[nmos::fields::nc::name(property)]; - if (data.is_null()) { sequence = value::array(); } - web::json::push_back(sequence, val); + // update property + modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) + { + auto& sequence = resource.data[nmos::fields::nc::name(property)]; + if (data.is_null()) { sequence = value::array(); } + web::json::push_back(sequence, val); - }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::sequence_item_added, val, sequence_item_index } })); + }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::sequence_item_added, val, sequence_item_index } })); - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, sequence_item_index); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, sequence_item_index); + } + catch (const nmos::control_protocol_exception& e) + { + slog::log(gate, SLOG_FLF) << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize() << " error: " << e.what(); + + return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); + } } // unknown property diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 529ced833..1a0814530 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -74,10 +74,10 @@ namespace nmos return value::null(); } - // constraints validation + // constraints validation, may throw nmos::control_protocol_exception // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring - bool constraints_validation(const web::json::value& data, const web::json::value& constraints) + void constraints_validation(const web::json::value& data, const web::json::value& constraints) { auto parameter_constraints_validation = [&constraints](const web::json::value& value) { @@ -85,74 +85,83 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber if (constraints.has_field(nmos::fields::nc::step) && !nmos::fields::nc::step(constraints).is_null()) { - if (!value.is_integer()) { return false; } + if (value.is_null()) { throw control_protocol_exception("value is null"); } + + if (!value.is_integer()) { throw control_protocol_exception("value is not an integer"); } const auto step = nmos::fields::nc::step(constraints).as_double(); - if (step <= 0) { return false; } + if (step <= 0) { throw control_protocol_exception("step is not a positive integer"); } const auto value_double = value.as_double(); if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) { auto min = nmos::fields::nc::minimum(constraints).as_double(); - if (0 != std::fmod(value_double - min, step)) { return false; } + if (0 != std::fmod(value_double - min, step)) { throw control_protocol_exception("value is not divisible by step"); } } else if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) { auto max = nmos::fields::nc::maximum(constraints).as_double(); - if (0 != std::fmod(max - value_double, step)) { return false; } + if (0 != std::fmod(max - value_double, step)) { throw control_protocol_exception("value is not divisible by step"); } } else { - if (0 != std::fmod(value_double, step)) { return false; } + if (0 != std::fmod(value_double, step)) { throw control_protocol_exception("value is not divisible by step"); } } } if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) { - if (!value.is_integer() || value.as_double() < nmos::fields::nc::minimum(constraints).as_double()) { return false; } + if (value.is_null()) { throw control_protocol_exception("value is null"); } + + if (!value.is_integer() || value.as_double() < nmos::fields::nc::minimum(constraints).as_double()) { throw control_protocol_exception("value is less than minimum"); } } if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) { - if (!value.is_integer() || value.as_double() > nmos::fields::nc::maximum(constraints).as_double()) { return false; } + if (value.is_null()) { throw control_protocol_exception("value is null"); } + + if (!value.is_integer() || value.as_double() > nmos::fields::nc::maximum(constraints).as_double()) { throw control_protocol_exception("value is greater than maximum"); } } // is string constraints // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring if (constraints.has_field(nmos::fields::nc::max_characters) && !constraints.at(nmos::fields::nc::max_characters).is_null()) { + if (value.is_null()) { throw control_protocol_exception("value is null"); } + const size_t max_characters = nmos::fields::nc::max_characters(constraints); - if (!value.is_string() || value.as_string().length() > max_characters) { return false; } + if (!value.is_string() || value.as_string().length() > max_characters) { throw control_protocol_exception("value is longer than maximum characters"); } } if (constraints.has_field(nmos::fields::nc::pattern) && !constraints.at(nmos::fields::nc::pattern).is_null()) { - if (!value.is_string()) { return false; } + if (value.is_null()) { throw control_protocol_exception("value is null"); } + + if (!value.is_string()) { throw control_protocol_exception("value is not a string"); } const auto value_string = utility::us2s(value.as_string()); bst::regex pattern(utility::us2s(nmos::fields::nc::pattern(constraints))); - if (!bst::regex_match(value_string, pattern)) { return false; } + if (!bst::regex_match(value_string, pattern)) { throw control_protocol_exception("value dose not match the pattern"); } } // reaching here, parameter validation successfully - return true; }; if (data.is_array()) { for (const auto& value : data.as_array()) { - if (!parameter_constraints_validation(value)) { return false; } + parameter_constraints_validation(value); } - // validation successfully - return true; } - - return parameter_constraints_validation(data); + else + { + parameter_constraints_validation(data); + } } - // level 0 datatype constraints validation + // level 0 datatype constraints validation, may throw nmos::control_protocol_exception // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - bool datatype_constraints_validation(const web::json::value& data, const datatype_constraints_validation_parameters& params) + void datatype_constraints_validation(const web::json::value& data, const datatype_constraints_validation_parameters& params) { // no constraints validation required - if (params.datatype_descriptor.is_null()) { return true; } + if (params.datatype_descriptor.is_null()) { return; } const auto& datatype_type = nmos::fields::nc::type(params.datatype_descriptor); @@ -161,57 +170,67 @@ namespace nmos { // hmm, for the primitive type, it should not have datatype constraints specified via the datatype_descriptor but just in case const auto& datatype_constraints = nmos::fields::nc::constraints(params.datatype_descriptor); - if (!datatype_constraints.is_null()) - { - return constraints_validation(data, datatype_constraints); - } - - auto primitive_validation = [](const nc_name& name, const web::json::value& value) + if (datatype_constraints.is_null()) { - auto is_int16 = [](int32_t value) - { - return value >= (std::numeric_limits::min)() - && value <= (std::numeric_limits::max)(); - }; - auto is_uint16 = [](uint32_t value) + auto primitive_validation = [](const nc_name& name, const web::json::value& value) { - return value >= (std::numeric_limits::min)() - && value <= (std::numeric_limits::max)(); - }; - auto is_float32 = [](double value) - { - return value >= (std::numeric_limits::min)() - && value <= (std::numeric_limits::max)(); + auto is_int16 = [](int32_t value) + { + return value >= (std::numeric_limits::min)() + && value <= (std::numeric_limits::max)(); + }; + auto is_uint16 = [](uint32_t value) + { + return value >= (std::numeric_limits::min)() + && value <= (std::numeric_limits::max)(); + }; + auto is_float32 = [](double value) + { + return value >= (std::numeric_limits::min)() + && value <= (std::numeric_limits::max)(); + }; + + if (U("NcBoolean") == name) { return value.is_boolean(); } + if (U("NcInt16") == name && value.is_number()) { return is_int16(value.as_number().to_int32()); } + if (U("NcInt32") == name && value.is_number()) { return value.as_number().is_int32(); } + if (U("NcInt64") == name && value.is_number()) { return value.as_number().is_int64(); } + if (U("NcUint16") == name && value.is_number()) { return is_uint16(value.as_number().to_uint32()); } + if (U("NcUint32") == name && value.is_number()) { return value.as_number().is_uint32(); } + if (U("NcUint64") == name && value.is_number()) { return value.as_number().is_uint64(); } + if (U("NcFloat32") == name && value.is_number()) { return is_float32(value.as_number().to_double()); } + if (U("NcFloat64") == name && value.is_number()) { return !value.as_number().is_integral(); } + if (U("NcString") == name) { return value.is_string(); } + + // invalid primitive type + return false; }; - if (U("NcBoolean") == name) { return value.is_boolean(); } - if (U("NcInt16") == name && value.is_number()) { return is_int16(value.as_number().to_int32()); } - if (U("NcInt32") == name && value.is_number()) { return value.as_number().is_int32(); } - if (U("NcInt64") == name && value.is_number()) { return value.as_number().is_int64(); } - if (U("NcUint16") == name && value.is_number()) { return is_uint16(value.as_number().to_uint32()); } - if (U("NcUint32") == name && value.is_number()) { return value.as_number().is_uint32(); } - if (U("NcUint64") == name && value.is_number()) { return value.as_number().is_uint64(); } - if (U("NcFloat32") == name && value.is_number()) { return is_float32(value.as_number().to_double()); } - if (U("NcFloat64") == name && value.is_number()) { return !value.as_number().is_integral(); } - if (U("NcString") == name) { return value.is_string(); } - - // invalid primitive type - return false; - }; - - // do primitive type constraints validation - const auto& name = nmos::fields::nc::name(params.datatype_descriptor); - if (data.is_array()) - { - for (const auto& value : data.as_array()) + // do primitive type constraints validation + const auto& name = nmos::fields::nc::name(params.datatype_descriptor); + if (data.is_array()) { - if (!primitive_validation(name, value)) { return false; } + for (const auto& value : data.as_array()) + { + if (!primitive_validation(name, value)) + { + throw control_protocol_exception("value is not a " + utility::us2s(name) + " type"); + } + } + } + else + { + if (!primitive_validation(name, data)) + { + throw control_protocol_exception("value is not a " + utility::us2s(name) + " type");; + } } - // reaching here, primitive validation successfully - return true; + } + else + { + constraints_validation(data, datatype_constraints); } - return primitive_validation(name, data); + return; } // do NcDatatypeDescriptorTypeDef constraints validation @@ -219,99 +238,112 @@ namespace nmos { // do the datatype constraints specified via the datatype_descriptor if presented const auto& datatype_constraints = nmos::fields::nc::constraints(params.datatype_descriptor); - if (!datatype_constraints.is_null()) { return constraints_validation(data, datatype_constraints); } + if (datatype_constraints.is_null()) + { + // do parent typename constraints validation + const auto& type_name = params.datatype_descriptor.at(nmos::fields::nc::parent_type); // parent type_name + datatype_constraints_validation(data, { details::get_datatype_descriptor(type_name, params.get_control_protocol_datatype), params.get_control_protocol_datatype }); + } + else + { + constraints_validation(data, datatype_constraints); + } - // do parent typename constraints validation - const auto& type_name = params.datatype_descriptor.at(nmos::fields::nc::parent_type); // parent type_name - if (!datatype_constraints_validation(data, { details::get_datatype_descriptor(type_name, params.get_control_protocol_datatype), params.get_control_protocol_datatype })) { return false; } + return; } // do NcDatatypeDescriptorEnum constraints validation if (nc_datatype_type::Enum == datatype_type) { const auto& items = nmos::fields::nc::items(params.datatype_descriptor); - return (items.end() != std::find_if(items.begin(), items.end(), [&](const web::json::value& nc_enum_item_descriptor) { return nmos::fields::nc::value(nc_enum_item_descriptor) == data; })); + if (items.end() == std::find_if(items.begin(), items.end(), [&](const web::json::value& nc_enum_item_descriptor) { return nmos::fields::nc::value(nc_enum_item_descriptor) == data; })) + { + const auto& name = nmos::fields::nc::name(params.datatype_descriptor); + throw control_protocol_exception("value is not an enum " + utility::us2s(name) + " type"); + } + + return; } // do NcDatatypeDescriptorStruct constraints validation if (nc_datatype_type::Struct == datatype_type) { + const auto& datatype_name = nmos::fields::nc::name(params.datatype_descriptor); const auto& fields = nmos::fields::nc::fields(params.datatype_descriptor); // NcFieldDescriptor for (const web::json::value& nc_field_descriptor : fields) { - const auto& name = nmos::fields::nc::name(nc_field_descriptor); - // check is the specific element in value strurcture - if (!data.has_field(name)) { return false; } + const auto& field_name = nmos::fields::nc::name(nc_field_descriptor); + // is field in strurcture + if (!data.has_field(field_name)) { throw control_protocol_exception("missing " + utility::us2s(field_name) + " in " + utility::us2s(datatype_name)); } - // check is the element is a nullable field - if (nmos::fields::nc::is_nullable(nc_field_descriptor) != data.is_null()) { return false; } + // is field nullable + if (nmos::fields::nc::is_nullable(nc_field_descriptor) != data.is_null()) { throw control_protocol_exception(utility::us2s(field_name) + " is not nullable"); } - // check is the element is a sequence field - if (nmos::fields::nc::is_sequence(nc_field_descriptor) != data.is_array()) { return false; } + // is field sequenceable + if (nmos::fields::nc::is_sequence(nc_field_descriptor) != data.is_array()) { throw control_protocol_exception(utility::us2s(field_name) + " is not sequenceable"); } // check against field constraints if presented const auto& constraints = nmos::fields::nc::constraints(nc_field_descriptor); - if (!constraints.is_null()) - { - auto value = data.at(name); - - // do field constraints validation - if (!constraints_validation(value, constraints)) { return false; } - } - else + if (constraints.is_null()) { // no field constraints, move to check the constraints of its typeName - const auto& type_name = nc_field_descriptor.at(nmos::fields::nc::type_name); + const auto& field_type_name = nc_field_descriptor.at(nmos::fields::nc::type_name); - if (!type_name.is_null()) + if (!field_type_name.is_null()) { - auto value = data.at(name); + auto value = data.at(field_name); if (value.is_array()) { for (const auto& val : value.as_array()) { // do typename constraints validation - if (!datatype_constraints_validation(val, { details::get_datatype_descriptor(type_name, params.get_control_protocol_datatype), params.get_control_protocol_datatype })) { return false; } + datatype_constraints_validation(val, { details::get_datatype_descriptor(field_type_name, params.get_control_protocol_datatype), params.get_control_protocol_datatype }); } } else { // do typename constraints validation - if (!datatype_constraints_validation(value, { details::get_datatype_descriptor(type_name, params.get_control_protocol_datatype), params.get_control_protocol_datatype })) { return false; } + datatype_constraints_validation(value, { details::get_datatype_descriptor(field_type_name, params.get_control_protocol_datatype), params.get_control_protocol_datatype }); } } } + else + { + // do field constraints validation + const auto& value = data.at(field_name); + constraints_validation(value, constraints); + } } - return true; + + return; } // unsupport datatype_type, no validation is required - return true; } - // multiple levels of constraints validation + // multiple levels of constraints validation, may throw nmos::control_protocol_exception // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - bool constraints_validation(const web::json::value& data, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) + void constraints_validation(const web::json::value& data, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) { // do level 2 runtime property constraints validation - if (!runtime_property_constraints.is_null()) { return details::constraints_validation(data, runtime_property_constraints); } + if (!runtime_property_constraints.is_null()) { constraints_validation(data, runtime_property_constraints); return; } // do level 1 property constraints validation - if (!property_constraints.is_null()) { return details::constraints_validation(data, property_constraints); } + if (!property_constraints.is_null()) { constraints_validation(data, property_constraints); return; } // do level 0 datatype constraints validation - return details::datatype_constraints_validation(data, params); + datatype_constraints_validation(data, params); } - // method parameter constraints validation - bool method_parameter_constraints_validation(const web::json::value& data, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) + // method parameter constraints validation, may throw nmos::control_protocol_exception + void method_parameter_constraints_validation(const web::json::value& data, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) { using web::json::value; // do level 1 property constraints & level 0 datatype constraints validation - return constraints_validation(data, value::null(), property_constraints, params); + constraints_validation(data, value::null(), property_constraints, params); } } @@ -589,7 +621,7 @@ namespace nmos } // method parameters constraints validation - bool method_parameters_contraints_validation(const web::json::value& arguments, const web::json::value& nc_method_descriptor, get_control_protocol_datatype_handler get_control_protocol_datatype) + void method_parameters_contraints_validation(const web::json::value& arguments, const web::json::value& nc_method_descriptor, get_control_protocol_datatype_handler get_control_protocol_datatype) { for (const auto& param : nmos::fields::nc::parameters(nc_method_descriptor)) { @@ -599,13 +631,9 @@ namespace nmos if (arguments.is_null() || !arguments.has_field(name)) { // missing argument parameter - return false; - } - if (!details::method_parameter_constraints_validation(arguments.at(name), constraints, { nmos::details::get_datatype_descriptor(type_name, get_control_protocol_datatype), get_control_protocol_datatype })) - { - return false; + throw control_protocol_exception("missing argument parameter " + utility::us2s(name)); } + details::method_parameter_constraints_validation(arguments.at(name), constraints, { nmos::details::get_datatype_descriptor(type_name, get_control_protocol_datatype), get_control_protocol_datatype }); } - return true; } } diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index 34ad7c593..9ec84379e 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -8,6 +8,11 @@ namespace nmos { struct control_protocol_resource; + struct control_protocol_exception : std::runtime_error + { + control_protocol_exception(const std::string& message) : std::runtime_error(message) {} + }; + namespace details { // get the runtime property constraints of a given property_id @@ -24,12 +29,12 @@ namespace nmos web::json::value datatype_descriptor; get_control_protocol_datatype_handler get_control_protocol_datatype; }; - // multiple levels of constraints validation + // multiple levels of constraints validation, may throw nmos::control_protocol_exception // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - bool constraints_validation(const web::json::value& value, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params); + void constraints_validation(const web::json::value& value, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params); - // method parameter constraints validation - bool method_parameter_constraints_validation(const web::json::value& data, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params); + // method parameter constraints validation, may throw nmos::control_protocol_exception + void method_parameter_constraints_validation(const web::json::value& data, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params); } // is the given class_id a NcBlock @@ -72,8 +77,8 @@ namespace nmos // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id resources::const_iterator find_control_protocol_resource(resources& resources, type type, const id& id); - // method parameters constraints validation - bool method_parameters_contraints_validation(const web::json::value& arguments, const web::json::value& nc_method_descriptor, get_control_protocol_datatype_handler get_control_protocol_datatype); + // method parameters constraints validation, may throw nmos::control_protocol_exception + void method_parameters_contraints_validation(const web::json::value& arguments, const web::json::value& nc_method_descriptor, get_control_protocol_datatype_handler get_control_protocol_datatype); } #endif diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 3660a6d06..df18eef3f 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -258,16 +258,18 @@ namespace nmos auto method = get_control_protocol_method(class_id, method_id); if (method.second) { - // do method arguments constraints validation - if (method_parameters_contraints_validation(arguments, method.first, get_control_protocol_datatype)) + try { + // do method arguments constraints validation + method_parameters_contraints_validation(arguments, method.first, get_control_protocol_datatype); + // execute the relevant method handler, then accumulating up their response to reponses response = method.second(resources, resource, handle, arguments, nmos::fields::nc::is_deprecated(method.first), get_control_protocol_class, get_control_protocol_datatype, gate); } - else + catch (const nmos::control_protocol_exception& e) { // invalid arguments - slog::log(gate, SLOG_FLF) << "invalid argument: " << arguments.serialize(); + slog::log(gate, SLOG_FLF) << "invalid argument: " << arguments.serialize() << " error: " << e.what(); response = make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); } } diff --git a/Development/nmos/test/control_protocol_test.cpp b/Development/nmos/test/control_protocol_test.cpp index 36c914809..65816413b 100644 --- a/Development/nmos/test/control_protocol_test.cpp +++ b/Development/nmos/test/control_protocol_test.cpp @@ -797,120 +797,120 @@ BST_TEST_CASE(testConstraints) // runtime property constraints validation const nmos::details::datatype_constraints_validation_parameters with_constraints_string_constraints_validation_params{ with_constraints_string_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE(nmos::details::constraints_validation(value::string(U("1234567890")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value::string(U("12345678901")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value::string(U("123456789A")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); - BST_REQUIRE(nmos::details::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("1234567890")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("12345678901")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ value::string(U("1234567890")), 1 }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), false); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value::string(U("1234567890")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("12345678901")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("123456789A")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("1234567890")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("12345678901")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890")), 1 }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); // property constraints validation - BST_REQUIRE(nmos::details::constraints_validation(value::string(U("abcde")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value::string(U("abcdef")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value::string(U("abcd1")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); - BST_REQUIRE(nmos::details::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcde")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcdef")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ value::string(U("abcde")), 1 }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), false); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value::string(U("abcde")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("abcdef")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("abcd1")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcde")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcdef")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("abcde")), 1 }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); // datatype constraints validation - BST_REQUIRE(nmos::details::constraints_validation(value::string(U("1a")), value::null(), value::null(), with_constraints_string_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value::string(U("1a2")), value::null(), value::null(), with_constraints_string_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value::string(U("1*")), value::null(), value::null(), with_constraints_string_constraints_validation_params), false); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value::string(U("1a")), value::null(), value::null(), with_constraints_string_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("1a2")), value::null(), value::null(), with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("1*")), value::null(), value::null(), with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); const nmos::details::datatype_constraints_validation_parameters no_constraints_string_constraints_validation_params{ no_constraints_string_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE(nmos::details::constraints_validation(value::string(U("1234567890-abcde-!\"£$%^&*()_+=")), value::null(), value::null(), no_constraints_string_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value::string(U("1234567890-abcde-!\"£$%^&*()_+=")), value::null(), value::null(), no_constraints_string_constraints_validation_params)); // number property constraints validation // runtime property constraints validation const nmos::details::datatype_constraints_validation_parameters with_constraints_int32_constraints_validation_params{ with_constraints_int32_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE(nmos::details::constraints_validation(10, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE(nmos::details::constraints_validation(1000, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(9, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(1001, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE(nmos::details::constraints_validation(value_of({ 10, 1000 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 10, 1001 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 10, value::string(U("a")) }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(10, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(1000, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(9, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(1001, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ 10, 1000 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 10, 1001 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 10, value::string(U("a")) }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); // property constraints validation - BST_REQUIRE(nmos::details::constraints_validation(50, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE(nmos::details::constraints_validation(500, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(45, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(505, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(499, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE(nmos::details::constraints_validation(value_of({ 50, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 49, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 50, 501 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 45, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 50, value::string(U("a")) }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(50, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(500, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(45, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(505, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(499, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ 50, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 49, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 50, 501 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 45, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 50, value::string(U("a")) }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); // datatype constraints validation - BST_REQUIRE(nmos::details::constraints_validation(100, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); - BST_REQUIRE(nmos::details::constraints_validation(250, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(90, value::null(), value::null(), with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(260, value::null(), value::null(), with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(99, value::null(), value::null(), with_constraints_int32_constraints_validation_params), false); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(100, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(250, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(90, value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(260, value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(99, value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); // int16 datatype constraints validation const nmos::details::datatype_constraints_validation_parameters no_constraints_int16_constraints_validation_params{ no_constraints_int16_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), false); - BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); - BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); // int32 datatype constraints validation const nmos::details::datatype_constraints_validation_parameters no_constraints_int32_constraints_validation_params{ no_constraints_int32_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), false); - BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); - BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); // int64 datatype constraints validation const nmos::details::datatype_constraints_validation_parameters no_constraints_int64_constraints_validation_params{ no_constraints_int64_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), false); - BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); - BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); // uint16 datatype constraints validation const nmos::details::datatype_constraints_validation_parameters no_constraints_uint16_constraints_validation_params{ no_constraints_uint16_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), false); - BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); - BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); // uint32 datatype constraints validation const nmos::details::datatype_constraints_validation_parameters no_constraints_uint32_constraints_validation_params{ no_constraints_uint32_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), false); - BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); - BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); // uint64 datatype constraints validation const nmos::details::datatype_constraints_validation_parameters no_constraints_uint64_constraints_validation_params{ no_constraints_uint64_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint64_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), false); - BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); - BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint64_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); // float32 datatype constraints validation const nmos::details::datatype_constraints_validation_parameters no_constraints_float32_constraints_validation_params{ no_constraints_float32_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), false); - BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); - BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); // float64 datatype constraints validation const nmos::details::datatype_constraints_validation_parameters no_constraints_float64_constraints_validation_params{ no_constraints_float64_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(1000, value::null(), value::null(), no_constraints_float64_constraints_validation_params), false); - BST_REQUIRE(nmos::details::constraints_validation(1000.0, value::null(), value::null(), no_constraints_float64_constraints_validation_params)); - BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); - BST_REQUIRE(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(1000, value::null(), value::null(), no_constraints_float64_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(1000.0, value::null(), value::null(), no_constraints_float64_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); // enum property datatype constraints validation const nmos::details::datatype_constraints_validation_parameters enum_constraints_validation_params{ enum_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE(nmos::details::constraints_validation(enum_value::foo, value::null(), value::null(), enum_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(4, value::null(), value::null(), enum_constraints_validation_params), false); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(enum_value::foo, value::null(), value::null(), enum_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(4, value::null(), value::null(), enum_constraints_validation_params), nmos::control_protocol_exception); // invalid data vs primitive datatype constraints const nmos::details::datatype_constraints_validation_parameters no_constraints_string_seq_constraints_validation_params{ no_constraints_string_seq_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); - BST_REQUIRE(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")), value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(1, value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), false); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")), value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(1, value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), nmos::control_protocol_exception); const nmos::details::datatype_constraints_validation_parameters no_constraints_int32_seq_constraints_validation_params{ no_constraints_int32_seq_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), false); - BST_REQUIRE(nmos::details::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); - BST_REQUIRE(nmos::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value::string(U("1234567890-abcde-!\"£$%^&*()_+=")), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_int32_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_string_constraints_validation_params), false); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"£$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("1234567890-abcde-!\"£$%^&*()_+=")), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); // struct property datatype constraints validation const auto good_struct = value_of({ @@ -1442,23 +1442,23 @@ BST_TEST_CASE(testConstraints) }); const nmos::details::datatype_constraints_validation_parameters struct_constraints_validation_params{ struct_datatype, nmos::make_get_control_protocol_datatype_handler(control_protocol_state) }; - BST_REQUIRE(nmos::details::constraints_validation(good_struct, value::null(), value::null(), struct_constraints_validation_params)); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct1, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_1, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_2, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_3, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_4, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_5, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_5_1, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_5_2, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_5_3, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_6, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_6_1, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_6_2, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_6_3, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_7, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_7_1, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_7_2, value::null(), value::null(), struct_constraints_validation_params), false); - BST_REQUIRE_EQUAL(nmos::details::constraints_validation(bad_struct2_7_3, value::null(), value::null(), struct_constraints_validation_params), false); + BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(good_struct, value::null(), value::null(), struct_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_4, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_5, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_5_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_5_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_5_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_6, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_6_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_6_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_6_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_7, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_7_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_7_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_7_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); } From 4f83f3822d47f98f9f3197733807c0600116e7c1 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 4 Jan 2024 10:49:51 +0000 Subject: [PATCH 083/250] Add property changed callback to perform application-specific operations to complete the property changed --- Development/nmos-cpp-node/main.cpp | 7 +-- .../nmos-cpp-node/node_implementation.cpp | 27 ++++++++++-- .../nmos/control_protocol_handlers.cpp | 7 +-- Development/nmos/control_protocol_handlers.h | 7 ++- Development/nmos/control_protocol_methods.cpp | 44 +++++++++++++------ Development/nmos/control_protocol_methods.h | 26 +++++------ Development/nmos/control_protocol_ws_api.cpp | 6 +-- Development/nmos/control_protocol_ws_api.h | 6 +-- Development/nmos/node_resources.cpp | 1 + Development/nmos/node_server.cpp | 2 +- Development/nmos/node_server.h | 5 ++- 11 files changed, 93 insertions(+), 45 deletions(-) diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index 159e45e12..d70a45958 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -139,9 +139,10 @@ int main(int argc, char* argv[]) nmos::experimental::control_protocol_state control_protocol_state; if (0 <= nmos::fields::control_protocol_ws_port(node_model.settings)) { - node_implementation.on_get_control_class(nmos::make_get_control_protocol_class_handler(control_protocol_state)); - node_implementation.on_get_control_datatype(nmos::make_get_control_protocol_datatype_handler(control_protocol_state)); - node_implementation.on_get_control_protocol_method(nmos::make_get_control_protocol_method_handler(control_protocol_state)); + node_implementation + .on_get_control_class(nmos::make_get_control_protocol_class_handler(control_protocol_state)) + .on_get_control_datatype(nmos::make_get_control_protocol_datatype_handler(control_protocol_state)) + .on_get_control_protocol_method(nmos::make_get_control_protocol_method_handler(control_protocol_state)); } // Set up the node server diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 035400aac..be0359e69 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1008,7 +1008,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr nmos::experimental::make_control_class_property(U("Example object sequence property"), { 3, 14 }, object_sequence, U("ExampleDataType"), false, false, true) }; - auto example_method_with_no_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + auto example_method_with_no_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -1016,7 +1016,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr return nmos::make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); }; - auto example_method_with_simple_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + auto example_method_with_simple_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... // and the method parameters constriants has already been validated by the outter function @@ -1025,7 +1025,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr return nmos::make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); }; - auto example_method_with_object_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + auto example_method_with_object_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... // and the method parameters constriants has already been validated by the outter function @@ -1696,6 +1696,24 @@ nmos::channelmapping_activation_handler make_node_implementation_channelmapping_ }; } +// Example Control Protocol WebSocket API property changed callback to perform application-specific operations to complete the property changed +nmos::control_protocol_property_changed_handler make_node_implementation_control_protocol_property_changed_handler(slog::base_gate& gate) +{ + return [&gate](const nmos::resource& resource, const utility::string_t& property_name, int index) + { + if (index >= 0) + { + // sequence property + slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Property: " << property_name << " index " << index << " has value changed to " << resource.data.at(property_name).at(index).serialize(); + } + else + { + // non-sequence property + slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Property: " << property_name << " has value changed to " << resource.data.at(property_name).serialize(); + } + }; +} + namespace impl { nmos::interlace_mode get_interlace_mode(const nmos::settings& settings) @@ -1849,5 +1867,6 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode .on_set_transportfile(make_node_implementation_transportfile_setter(model.node_resources, model.settings)) .on_connection_activated(make_node_implementation_connection_activation_handler(model, gate)) .on_validate_channelmapping_output_map(make_node_implementation_map_validator()) // may be omitted if not required - .on_channelmapping_activated(make_node_implementation_channelmapping_activation_handler(gate)); + .on_channelmapping_activated(make_node_implementation_channelmapping_activation_handler(gate)) + .on_control_protocol_property_changed(make_node_implementation_control_protocol_property_changed_handler(gate)); // may be omitted if IS-12 not required } diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index f4e0129dd..f9650a21a 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -69,6 +69,7 @@ namespace nmos }; } + // Example Receiver-Monitor Connection activation callback to perform application-specific operations to complete activation control_protocol_connection_activation_handler make_receiver_monitor_connection_activation_handler(resources& resources) { return [&resources](const resource& connection_resource) @@ -79,18 +80,18 @@ namespace nmos // update receiver-monitor's connectionStatus propertry const auto active = nmos::fields::master_enable(nmos::fields::endpoint_active(connection_resource.data)); - const web::json::value val = active ? nc_connection_status::connected : nc_connection_status::disconnected; + const web::json::value value = active ? nc_connection_status::connected : nc_connection_status::disconnected; // hmm, maybe updating connectionStatusMessage, payloadStatus, and payloadStatusMessage too const auto propertry_changed_event = make_propertry_changed_event(nmos::fields::nc::oid(found->data), { - { nc_receiver_monitor_connection_status_property_id, nc_property_change_type::type::value_changed, val } + { nc_receiver_monitor_connection_status_property_id, nc_property_change_type::type::value_changed, value } }); modify_control_protocol_resource(resources, found->id, [&](nmos::resource& resource) { - resource.data[nmos::fields::nc::connection_status] = val; + resource.data[nmos::fields::nc::connection_status] = value; }, propertry_changed_event); } diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index 580a7a2ca..b98766b83 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -32,10 +32,15 @@ namespace nmos // this callback should not throw exceptions typedef std::function get_control_protocol_datatype_handler; + // a control_protocol_property_changed_handler is a notification that the specified (IS-12) property has changed + // index is set to -1 for non-sequence property + // this callback should not throw exceptions, as the relevant property will already has been changed and those changes will not be rolled back + typedef std::function control_protocol_property_changed_handler; + namespace experimental { // method handler definition - typedef std::function method_handler; + typedef std::function method_handler; // method definition (NcMethodDescriptor vs method handler) typedef std::pair method; diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index bfde62701..a6eed1807 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -14,7 +14,7 @@ namespace nmos { // NcObject methods implementation // Get property value - web::json::value get(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value get(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -36,7 +36,7 @@ namespace nmos } // Set property value - web::json::value set(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + web::json::value set(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -72,6 +72,12 @@ namespace nmos { resource.data[nmos::fields::nc::name(property)] = val; + // do notification that the specified property has changed + if (property_changed) + { + property_changed(resource, nmos::fields::nc::name(property), -1); + } + }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::value_changed, val } })); return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); @@ -91,7 +97,7 @@ namespace nmos } // Get sequence item - web::json::value get_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value get_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -132,7 +138,7 @@ namespace nmos } // Set sequence item - web::json::value set_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + web::json::value set_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -174,6 +180,12 @@ namespace nmos { resource.data[nmos::fields::nc::name(property)][index] = val; + // do notification that the specified property has changed + if (property_changed) + { + property_changed(resource, nmos::fields::nc::name(property), index); + } + }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::sequence_item_changed, val, nc_id(index) } })); return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); @@ -199,7 +211,7 @@ namespace nmos } // Add item to sequence - web::json::value add_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + web::json::value add_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -244,6 +256,12 @@ namespace nmos if (data.is_null()) { sequence = value::array(); } web::json::push_back(sequence, val); + // do notification that the specified property has changed + if (property_changed) + { + property_changed(resource, nmos::fields::nc::name(property), sequence.as_array().size()-1); + } + }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::sequence_item_added, val, sequence_item_index } })); return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, sequence_item_index); @@ -263,7 +281,7 @@ namespace nmos } // Delete sequence item - web::json::value remove_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value remove_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -311,7 +329,7 @@ namespace nmos } // Get sequence length - web::json::value get_sequence_length(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value get_sequence_length(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -366,7 +384,7 @@ namespace nmos // NcBlock methods implementation // Gets descriptors of members of the block - web::json::value get_member_descriptors(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value get_member_descriptors(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -383,7 +401,7 @@ namespace nmos } // Finds member(s) by path - web::json::value find_members_by_path(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value find_members_by_path(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -441,7 +459,7 @@ namespace nmos } // Finds members with given role name or fragment - web::json::value find_members_by_role(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value find_members_by_role(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -467,7 +485,7 @@ namespace nmos } // Finds members with given class id - web::json::value find_members_by_class_id(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value find_members_by_class_id(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -495,7 +513,7 @@ namespace nmos // NcClassManager methods implementation // Get a single class descriptor - web::json::value get_control_class(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate) + web::json::value get_control_class(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { using web::json::value; @@ -550,7 +568,7 @@ namespace nmos } // Get a single datatype descriptor - web::json::value get_datatype(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate) + web::json::value get_datatype(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler get_control_protocol_datatype, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... diff --git a/Development/nmos/control_protocol_methods.h b/Development/nmos/control_protocol_methods.h index d0eec3ca7..e686c702b 100644 --- a/Development/nmos/control_protocol_methods.h +++ b/Development/nmos/control_protocol_methods.h @@ -15,35 +15,35 @@ namespace nmos { // NcObject methods implementation // Get property value - web::json::value get(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value get(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // Set property value - web::json::value set(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value set(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); // Get sequence item - web::json::value get_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value get_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // Set sequence item - web::json::value set_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value set_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); // Add item to sequence - web::json::value add_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value add_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); // Delete sequence item - web::json::value remove_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value remove_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // Get sequence length - web::json::value get_sequence_length(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value get_sequence_length(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // NcBlock methods implementation // Get descriptors of members of the block - web::json::value get_member_descriptors(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value get_member_descriptors(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // Finds member(s) by path - web::json::value find_members_by_path(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value find_members_by_path(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // Finds members with given role name or fragment - web::json::value find_members_by_role(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value find_members_by_role(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // Finds members with given class id - web::json::value find_members_by_class_id(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value find_members_by_class_id(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // NcClassManager methods implementation // Get a single class descriptor - web::json::value get_control_class(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, slog::base_gate& gate); + web::json::value get_control_class(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // Get a single datatype descriptor - web::json::value get_datatype(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler get_control_protocol_datatype, slog::base_gate& gate); + web::json::value get_datatype(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler get_control_protocol_datatype, control_protocol_property_changed_handler, slog::base_gate& gate); } } diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index df18eef3f..b6bc3f476 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -184,12 +184,12 @@ namespace nmos }; } - web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_method_handler get_control_protocol_method, slog::base_gate& gate_) + web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_method_handler get_control_protocol_method, nmos::control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) { using web::json::value; using web::json::value_of; - return [&model, &websockets, get_control_protocol_class, get_control_protocol_datatype, get_control_protocol_method, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id, const web::websockets::websocket_incoming_message& msg_) + return [&model, &websockets, get_control_protocol_class, get_control_protocol_datatype, get_control_protocol_method, property_changed, &gate_](const web::uri& connection_uri, const web::websockets::experimental::listener::connection_id& connection_id, const web::websockets::websocket_incoming_message& msg_) { nmos::ws_api_gate gate(gate_, connection_uri); @@ -264,7 +264,7 @@ namespace nmos method_parameters_contraints_validation(arguments, method.first, get_control_protocol_datatype); // execute the relevant method handler, then accumulating up their response to reponses - response = method.second(resources, resource, handle, arguments, nmos::fields::nc::is_deprecated(method.first), get_control_protocol_class, get_control_protocol_datatype, gate); + response = method.second(resources, resource, handle, arguments, nmos::fields::nc::is_deprecated(method.first), get_control_protocol_class, get_control_protocol_datatype, property_changed, gate); } catch (const nmos::control_protocol_exception& e) { diff --git a/Development/nmos/control_protocol_ws_api.h b/Development/nmos/control_protocol_ws_api.h index bbb686272..1d8052cd8 100644 --- a/Development/nmos/control_protocol_ws_api.h +++ b/Development/nmos/control_protocol_ws_api.h @@ -16,15 +16,15 @@ namespace nmos web::websockets::experimental::listener::validate_handler make_control_protocol_ws_validate_handler(nmos::node_model& model, slog::base_gate& gate); web::websockets::experimental::listener::open_handler make_control_protocol_ws_open_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate); web::websockets::experimental::listener::close_handler make_control_protocol_ws_close_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate); - web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_method_handler get_control_protocol_method, slog::base_gate& gate); + web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_method_handler get_control_protocol_method, nmos::control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - inline web::websockets::experimental::listener::websocket_listener_handlers make_control_protocol_ws_api(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_method_handler get_control_protocol_method, slog::base_gate& gate) + inline web::websockets::experimental::listener::websocket_listener_handlers make_control_protocol_ws_api(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_method_handler get_control_protocol_method, nmos::control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { return{ nmos::make_control_protocol_ws_validate_handler(model, gate), nmos::make_control_protocol_ws_open_handler(model, websockets, gate), nmos::make_control_protocol_ws_close_handler(model, websockets, gate), - nmos::make_control_protocol_ws_message_handler(model, websockets, get_control_protocol_class, get_control_protocol_datatype, get_control_protocol_method, gate) + nmos::make_control_protocol_ws_message_handler(model, websockets, get_control_protocol_class, get_control_protocol_datatype, get_control_protocol_method, property_changed, gate) }; } diff --git a/Development/nmos/node_resources.cpp b/Development/nmos/node_resources.cpp index 9cd342137..216ef7db2 100644 --- a/Development/nmos/node_resources.cpp +++ b/Development/nmos/node_resources.cpp @@ -134,6 +134,7 @@ namespace nmos { for (const auto& version : nmos::is12_versions::from_settings(settings)) { + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/IS-04_interactions.html auto ncp_uri = web::uri_builder() .set_scheme(nmos::ws_scheme(settings)) .set_port(nmos::fields::control_protocol_ws_port(settings)) diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index 277c448dd..3cb4836d0 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -78,7 +78,7 @@ namespace nmos { if (control_protocol_ws_port == events_ws_port) throw std::runtime_error("Same port used for events and control protocol websockets are not supported"); auto& control_protocol_ws_api = node_server.ws_handlers[{ {}, control_protocol_ws_port }]; - control_protocol_ws_api.first = nmos::make_control_protocol_ws_api(node_model, control_protocol_ws_api.second, node_implementation.get_control_protocol_class, node_implementation.get_control_protocol_datatype, node_implementation.get_control_protocol_method, gate); + control_protocol_ws_api.first = nmos::make_control_protocol_ws_api(node_model, control_protocol_ws_api.second, node_implementation.get_control_protocol_class, node_implementation.get_control_protocol_datatype, node_implementation.get_control_protocol_method, node_implementation.control_protocol_property_changed, gate); } // Set up the listeners for each HTTP API port diff --git a/Development/nmos/node_server.h b/Development/nmos/node_server.h index 01e4d44a5..1a7ecb77c 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -27,7 +27,7 @@ namespace nmos // underlying implementation into the server instance for the NMOS Node struct node_implementation { - node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_method_handler get_control_protocol_method) + node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_method_handler get_control_protocol_method, nmos::control_protocol_property_changed_handler control_protocol_property_changed) : load_server_certificates(std::move(load_server_certificates)) , load_dh_param(std::move(load_dh_param)) , load_ca_certificates(std::move(load_ca_certificates)) @@ -49,6 +49,7 @@ namespace nmos , get_control_protocol_class(std::move(get_control_protocol_class)) , get_control_protocol_datatype(std::move(get_control_protocol_datatype)) , get_control_protocol_method(std::move(get_control_protocol_method)) + , control_protocol_property_changed(std::move(control_protocol_property_changed)) {} // use the default constructor and chaining member functions for fluent initialization @@ -80,6 +81,7 @@ namespace nmos node_implementation& on_get_control_class(nmos::get_control_protocol_class_handler get_control_protocol_class) { this->get_control_protocol_class = std::move(get_control_protocol_class); return *this; } node_implementation& on_get_control_datatype(nmos::get_control_protocol_datatype_handler get_control_protocol_datatype) { this->get_control_protocol_datatype = std::move(get_control_protocol_datatype); return *this; } node_implementation& on_get_control_protocol_method(nmos::get_control_protocol_method_handler get_control_protocol_method) { this->get_control_protocol_method = std::move(get_control_protocol_method); return *this; } + node_implementation& on_control_protocol_property_changed(nmos::control_protocol_property_changed_handler control_protocol_property_changed) { this->control_protocol_property_changed = std::move(control_protocol_property_changed); return *this; } // deprecated, use on_validate_connection_resource_patch node_implementation& on_validate_merged(nmos::details::connection_resource_patch_validator validate_merged) { return on_validate_connection_resource_patch(std::move(validate_merged)); } @@ -121,6 +123,7 @@ namespace nmos nmos::get_control_protocol_class_handler get_control_protocol_class; nmos::get_control_protocol_datatype_handler get_control_protocol_datatype; nmos::get_control_protocol_method_handler get_control_protocol_method; + nmos::control_protocol_property_changed_handler control_protocol_property_changed; }; // Construct a server instance for an NMOS Node, implementing the IS-04 Node API, IS-05 Connection API, IS-07 Events API From ea732dac3e6d27f2d6123ce96406da82b9a54aeb Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 4 Jan 2024 12:04:31 +0000 Subject: [PATCH 084/250] Add authorization support to IS-12 --- Development/nmos-cpp-node/main.cpp | 2 +- Development/nmos/control_protocol_ws_api.cpp | 8 ++++++-- Development/nmos/control_protocol_ws_api.h | 7 ++++--- Development/nmos/node_server.cpp | 2 +- Development/nmos/scope.h | 3 +++ 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index d70a45958..b08ba5ef8 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -118,7 +118,7 @@ int main(int argc, char* argv[]) #endif // only implement communication with Authorization server if IS-10/BCP-003-02 is required -// cf. preprocessor conditions in nmos::make_node_api, nmos::make_connection_api, nmos::make_events_api, nmos::make_channelmapping_api, make_events_ws_validate_handler +// cf. preprocessor conditions in nmos::make_node_api, nmos::make_connection_api, nmos::make_events_api, nmos::make_channelmapping_api, make_events_ws_validate_handler, make_control_protocol_ws_validate_handler nmos::experimental::authorization_state authorization_state; if (nmos::experimental::fields::server_authorization(node_model.settings)) { diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index b6bc3f476..890efe761 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -50,9 +50,9 @@ namespace nmos // IS-12 Control Protocol WebSocket API - web::websockets::experimental::listener::validate_handler make_control_protocol_ws_validate_handler(nmos::node_model& model, slog::base_gate& gate_) + web::websockets::experimental::listener::validate_handler make_control_protocol_ws_validate_handler(nmos::node_model& model, nmos::experimental::ws_validate_authorization_handler ws_validate_authorization, slog::base_gate& gate_) { - return [&model, &gate_](web::http::http_request req) + return [&model, ws_validate_authorization, &gate_](web::http::http_request req) { nmos::ws_api_gate gate(gate_, req.request_uri()); @@ -60,6 +60,10 @@ namespace nmos // Clients SHOULD use the "Authorization Request Header Field" method. // Clients MAY use a "URI Query Parameter". // See https://tools.ietf.org/html/rfc6750#section-2 + if (ws_validate_authorization) + { + if (!ws_validate_authorization(req, nmos::experimental::scopes::ncp)) { return false; } + } // For now just return true const auto& ws_ncp_path = req.request_uri().path(); diff --git a/Development/nmos/control_protocol_ws_api.h b/Development/nmos/control_protocol_ws_api.h index 1d8052cd8..1bf3c556a 100644 --- a/Development/nmos/control_protocol_ws_api.h +++ b/Development/nmos/control_protocol_ws_api.h @@ -3,6 +3,7 @@ #include "nmos/control_protocol_handlers.h" #include "nmos/websockets.h" +#include "nmos/ws_api_utils.h" namespace slog { @@ -13,15 +14,15 @@ namespace nmos { struct node_model; - web::websockets::experimental::listener::validate_handler make_control_protocol_ws_validate_handler(nmos::node_model& model, slog::base_gate& gate); + web::websockets::experimental::listener::validate_handler make_control_protocol_ws_validate_handler(nmos::node_model& model, nmos::experimental::ws_validate_authorization_handler ws_validate_authorization, slog::base_gate& gate); web::websockets::experimental::listener::open_handler make_control_protocol_ws_open_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate); web::websockets::experimental::listener::close_handler make_control_protocol_ws_close_handler(nmos::node_model& model, nmos::websockets& websockets, slog::base_gate& gate); web::websockets::experimental::listener::message_handler make_control_protocol_ws_message_handler(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_method_handler get_control_protocol_method, nmos::control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - inline web::websockets::experimental::listener::websocket_listener_handlers make_control_protocol_ws_api(nmos::node_model& model, nmos::websockets& websockets, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_method_handler get_control_protocol_method, nmos::control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + inline web::websockets::experimental::listener::websocket_listener_handlers make_control_protocol_ws_api(nmos::node_model& model, nmos::websockets& websockets, nmos::experimental::ws_validate_authorization_handler ws_validate_authorization, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::get_control_protocol_method_handler get_control_protocol_method, nmos::control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { return{ - nmos::make_control_protocol_ws_validate_handler(model, gate), + nmos::make_control_protocol_ws_validate_handler(model, ws_validate_authorization, gate), nmos::make_control_protocol_ws_open_handler(model, websockets, gate), nmos::make_control_protocol_ws_close_handler(model, websockets, gate), nmos::make_control_protocol_ws_message_handler(model, websockets, get_control_protocol_class, get_control_protocol_datatype, get_control_protocol_method, property_changed, gate) diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index 3cb4836d0..5cb290d50 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -78,7 +78,7 @@ namespace nmos { if (control_protocol_ws_port == events_ws_port) throw std::runtime_error("Same port used for events and control protocol websockets are not supported"); auto& control_protocol_ws_api = node_server.ws_handlers[{ {}, control_protocol_ws_port }]; - control_protocol_ws_api.first = nmos::make_control_protocol_ws_api(node_model, control_protocol_ws_api.second, node_implementation.get_control_protocol_class, node_implementation.get_control_protocol_datatype, node_implementation.get_control_protocol_method, node_implementation.control_protocol_property_changed, gate); + control_protocol_ws_api.first = nmos::make_control_protocol_ws_api(node_model, control_protocol_ws_api.second, node_implementation.ws_validate_authorization, node_implementation.get_control_protocol_class, node_implementation.get_control_protocol_datatype, node_implementation.get_control_protocol_method, node_implementation.control_protocol_property_changed, gate); } // Set up the listeners for each HTTP API port diff --git a/Development/nmos/scope.h b/Development/nmos/scope.h index 1f3999531..25d65004e 100644 --- a/Development/nmos/scope.h +++ b/Development/nmos/scope.h @@ -24,6 +24,8 @@ namespace nmos const scope events{ U("events") }; // IS-08 const scope channelmapping{ U("channelmapping") }; + // IS-12 + const scope ncp{ U("ncp") }; } inline utility::string_t make_scope(const scope& scope) @@ -40,6 +42,7 @@ namespace nmos if (scopes::netctrl.name == scope) { return scopes::netctrl; } if (scopes::events.name == scope) { return scopes::events; } if (scopes::channelmapping.name == scope) { return scopes::channelmapping; } + if (scopes::ncp.name == scope) { return scopes::ncp; } return{}; } } From 6fd4e7805cde125953d4fd6cb946350eb7d15d92 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Mon, 8 Jan 2024 23:48:59 +0000 Subject: [PATCH 085/250] Prevent warning C26800 --- .../nmos-cpp-node/node_implementation.cpp | 6 +- Development/nmos/control_protocol_handlers.h | 2 +- Development/nmos/control_protocol_methods.cpp | 816 +++++++++--------- Development/nmos/control_protocol_methods.h | 61 +- Development/nmos/control_protocol_state.cpp | 26 +- Development/nmos/control_protocol_utils.cpp | 39 +- Development/nmos/control_protocol_utils.h | 6 +- Development/nmos/control_protocol_ws_api.cpp | 2 +- 8 files changed, 482 insertions(+), 476 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index be0359e69..486ac35d0 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1008,7 +1008,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr nmos::experimental::make_control_class_property(U("Example object sequence property"), { 3, 14 }, object_sequence, U("ExampleDataType"), false, false, true) }; - auto example_method_with_no_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::control_protocol_property_changed_handler, slog::base_gate& gate) + auto example_method_with_no_args = [](nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -1016,7 +1016,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr return nmos::make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); }; - auto example_method_with_simple_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::control_protocol_property_changed_handler, slog::base_gate& gate) + auto example_method_with_simple_args = [](nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... // and the method parameters constriants has already been validated by the outter function @@ -1025,7 +1025,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr return nmos::make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); }; - auto example_method_with_object_args = [](nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::control_protocol_property_changed_handler, slog::base_gate& gate) + auto example_method_with_object_args = [](nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, nmos::get_control_protocol_class_handler get_control_protocol_class, nmos::get_control_protocol_datatype_handler get_control_protocol_datatype, nmos::control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... // and the method parameters constriants has already been validated by the outter function diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index b98766b83..516d9c21a 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -40,7 +40,7 @@ namespace nmos namespace experimental { // method handler definition - typedef std::function method_handler; + typedef std::function method_handler; // method definition (NcMethodDescriptor vs method handler) typedef std::pair method; diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index a6eed1807..bfa3125a7 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -10,565 +10,566 @@ namespace nmos { - namespace details + // NcObject methods implementation + // Get property value + web::json::value get(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { - // NcObject methods implementation - // Get property value - web::json::value get(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - const auto& property_id = nmos::fields::nc::id(arguments); + const auto& property_id = nmos::fields::nc::id(arguments); - slog::log(gate, SLOG_FLF) << "Get property: " << property_id.serialize(); - - // find the relevant nc_property_descriptor - const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); - if (!property.is_null()) - { - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(property))); - } + slog::log(gate, SLOG_FLF) << "Get property: " << property_id.serialize(); - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do Get"); - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + // find the relevant nc_property_descriptor + const auto& property = find_property(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class); + if (!property.is_null()) + { + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, resource.data.at(nmos::fields::nc::name(property))); } - // Set property value - web::json::value set(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do Get"); + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + } + + // Set property value + web::json::value set(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& val = nmos::fields::nc::value(arguments); + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& val = nmos::fields::nc::value(arguments); - slog::log(gate, SLOG_FLF) << "Set property: " << property_id.serialize() << " value: " << val.serialize(); + slog::log(gate, SLOG_FLF) << "Set property: " << property_id.serialize() << " value: " << val.serialize(); - // find the relevant nc_property_descriptor - const auto property_id_ = parse_nc_property_id(property_id); - const auto& property = find_property(property_id_, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); - if (!property.is_null()) + // find the relevant nc_property_descriptor + const auto property_id_ = details::parse_nc_property_id(property_id); + const auto& property = find_property(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class); + if (!property.is_null()) + { + if (nmos::fields::nc::is_read_only(property)) { - if (nmos::fields::nc::is_read_only(property)) - { - return make_control_protocol_message_response(handle, { nc_method_status::read_only }); - } + return make_control_protocol_message_response(handle, { nc_method_status::read_only }); + } - if ((val.is_null() && !nmos::fields::nc::is_nullable(property)) - || (!val.is_array() && nmos::fields::nc::is_sequence(property)) - || (val.is_array() && !nmos::fields::nc::is_sequence(property))) - { - return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); - } + if ((val.is_null() && !nmos::fields::nc::is_nullable(property)) + || (!val.is_array() && nmos::fields::nc::is_sequence(property)) + || (val.is_array() && !nmos::fields::nc::is_sequence(property))) + { + return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); + } - try + try + { + // do property constraints validation + nmos::details::constraints_validation(val, details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype }); + + // update property + modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource) { - // do property constraints validation - nmos::details::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype }); + resource.data[nmos::fields::nc::name(property)] = val; - // update property - modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) + // do notification that the specified property has changed + if (property_changed) { - resource.data[nmos::fields::nc::name(property)] = val; - - // do notification that the specified property has changed - if (property_changed) - { - property_changed(resource, nmos::fields::nc::name(property), -1); - } - - }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::value_changed, val } })); + property_changed(resource, nmos::fields::nc::name(property), -1); + } - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); - } - catch (const nmos::control_protocol_exception& e) - { - slog::log(gate, SLOG_FLF) << "Set property: " << property_id.serialize() << " value: " << val.serialize() << " error: " << e.what(); + }, make_propertry_changed_event(nmos::fields::nc::oid(resource.data), { { property_id_, nc_property_change_type::type::value_changed, val } })); - return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); - } + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); } + catch (const nmos::control_protocol_exception& e) + { + slog::log(gate, SLOG_FLF) << "Set property: " << property_id.serialize() << " value: " << val.serialize() << " error: " << e.what(); - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << " to do Set"; - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); + } } - // Get sequence item - web::json::value get_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& index = nmos::fields::nc::index(arguments); + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << " to do Set"; + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + } - slog::log(gate, SLOG_FLF) << "Get sequence item: " << property_id.serialize() << " index: " << index; + // Get sequence item + web::json::value get_sequence_item(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - // find the relevant nc_property_descriptor - const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); - if (!property.is_null()) - { - const auto& data = resource->data.at(nmos::fields::nc::name(property)); + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& index = nmos::fields::nc::index(arguments); - if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) - { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); - } + slog::log(gate, SLOG_FLF) << "Get sequence item: " << property_id.serialize() << " index: " << index; - if (data.as_array().size() > (size_t)index) - { - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, data.at(index)); - } + // find the relevant nc_property_descriptor + const auto& property = find_property(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class); + if (!property.is_null()) + { + const auto& data = resource.data.at(nmos::fields::nc::name(property)); - // out of bound + if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) + { + // property is not a sequence utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do GetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); } - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do GetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); - } - - // Set sequence item - web::json::value set_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& index = nmos::fields::nc::index(arguments); - const auto& val = nmos::fields::nc::value(arguments); - - slog::log(gate, SLOG_FLF) << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize(); - - // find the relevant nc_property_descriptor - const auto property_id_ = parse_nc_property_id(property_id); - const auto& property = find_property(property_id_, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); - if (!property.is_null()) + if (data.as_array().size() > (size_t)index) { - if (nmos::fields::nc::is_read_only(property)) - { - return make_control_protocol_message_response(handle, { nc_method_status::read_only }); - } - - auto& data = resource->data.at(nmos::fields::nc::name(property)); - - if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) - { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do SetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); - } - - if (data.as_array().size() > (size_t)index) - { - try - { - // do property constraints validation - nmos::details::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype }); - - // update property - modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) - { - resource.data[nmos::fields::nc::name(property)][index] = val; - - // do notification that the specified property has changed - if (property_changed) - { - property_changed(resource, nmos::fields::nc::name(property), index); - } - - }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::sequence_item_changed, val, nc_id(index) } })); - - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); - } - catch (const nmos::control_protocol_exception& e) - { - slog::log(gate, SLOG_FLF) << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize() << " error: " << e.what(); - - return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); - } - } - - // out of bound - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do SetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, data.at(index)); } - // unknown property + // out of bound utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do SetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do GetSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); } - // Add item to sequence - web::json::value add_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do GetSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + } - using web::json::value; + // Set sequence item + web::json::value set_sequence_item(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& val = nmos::fields::nc::value(arguments); + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& index = nmos::fields::nc::index(arguments); + const auto& val = nmos::fields::nc::value(arguments); - slog::log(gate, SLOG_FLF) << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize(); + slog::log(gate, SLOG_FLF) << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize(); - // find the relevant nc_property_descriptor - const auto property_id_ = parse_nc_property_id(property_id); - const auto& property = find_property(property_id_, parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); - if (!property.is_null()) + // find the relevant nc_property_descriptor + const auto property_id_ = details::parse_nc_property_id(property_id); + const auto& property = find_property(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class); + if (!property.is_null()) + { + if (nmos::fields::nc::is_read_only(property)) { - if (nmos::fields::nc::is_read_only(property)) - { - return make_control_protocol_message_response(handle, { nc_method_status::read_only }); - } - - if (!nmos::fields::nc::is_sequence(property)) - { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do AddSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); - } + return make_control_protocol_message_response(handle, { nc_method_status::read_only }); + } - auto& data = resource->data.at(nmos::fields::nc::name(property)); + auto& data = resource.data.at(nmos::fields::nc::name(property)); - const nc_id sequence_item_index = data.is_null() ? 0 : nc_id(data.as_array().size()); + if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do SetSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); + } + if (data.as_array().size() > (size_t)index) + { try { // do property constraints validation - nmos::details::constraints_validation(val, get_runtime_property_constraints(property_id_, resource->data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype }); + nmos::details::constraints_validation(val, details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype }); // update property - modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) + modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource) { - auto& sequence = resource.data[nmos::fields::nc::name(property)]; - if (data.is_null()) { sequence = value::array(); } - web::json::push_back(sequence, val); + resource.data[nmos::fields::nc::name(property)][index] = val; // do notification that the specified property has changed if (property_changed) { - property_changed(resource, nmos::fields::nc::name(property), sequence.as_array().size()-1); + property_changed(resource, nmos::fields::nc::name(property), index); } - }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { property_id_, nc_property_change_type::type::sequence_item_added, val, sequence_item_index } })); + }, make_propertry_changed_event(nmos::fields::nc::oid(resource.data), { { property_id_, nc_property_change_type::type::sequence_item_changed, val, nc_id(index) } })); - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, sequence_item_index); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); } catch (const nmos::control_protocol_exception& e) { - slog::log(gate, SLOG_FLF) << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize() << " error: " << e.what(); + slog::log(gate, SLOG_FLF) << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize() << " error: " << e.what(); return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); } } - // unknown property + // out of bound utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do AddSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do SetSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); } - // Delete sequence item - web::json::value remove_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do SetSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + } + + // Add item to sequence + web::json::value add_sequence_item(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler get_control_protocol_datatype, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + using web::json::value; + + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& val = nmos::fields::nc::value(arguments); + + slog::log(gate, SLOG_FLF) << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize(); + + // find the relevant nc_property_descriptor + const auto property_id_ = details::parse_nc_property_id(property_id); + const auto& property = find_property(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class); + if (!property.is_null()) { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + if (nmos::fields::nc::is_read_only(property)) + { + return make_control_protocol_message_response(handle, { nc_method_status::read_only }); + } - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& index = nmos::fields::nc::index(arguments); + if (!nmos::fields::nc::is_sequence(property)) + { + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do AddSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); + } - slog::log(gate, SLOG_FLF) << "Remove sequence item: " << property_id.serialize() << " index: " << index; + auto& data = resource.data.at(nmos::fields::nc::name(property)); - // find the relevant nc_property_descriptor - const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); - if (!property.is_null()) + const nc_id sequence_item_index = data.is_null() ? 0 : nc_id(data.as_array().size()); + + try { - const auto& data = resource->data.at(nmos::fields::nc::name(property)); + // do property constraints validation + nmos::details::constraints_validation(val, details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype), get_control_protocol_datatype }); - if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) + // update property + modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource) { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do RemoveSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); - } + auto& sequence = resource.data[nmos::fields::nc::name(property)]; + if (data.is_null()) { sequence = value::array(); } + web::json::push_back(sequence, val); - if (data.as_array().size() > (size_t)index) - { - modify_control_protocol_resource(resources, resource->id, [&](nmos::resource& resource) + // do notification that the specified property has changed + if (property_changed) { - auto& sequence = resource.data[nmos::fields::nc::name(property)].as_array(); - sequence.erase(index); + property_changed(resource, nmos::fields::nc::name(property), (int)sequence.as_array().size()-1); + } - }, make_propertry_changed_event(nmos::fields::nc::oid(resource->data), { { parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_removed, nc_id(index) } })); + }, make_propertry_changed_event(nmos::fields::nc::oid(resource.data), { { property_id_, nc_property_change_type::type::sequence_item_added, val, sequence_item_index } })); - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); - } + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, sequence_item_index); + } + catch (const nmos::control_protocol_exception& e) + { + slog::log(gate, SLOG_FLF) << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize() << " error: " << e.what(); + + return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); + } + } + + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do AddSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + } - // out of bound + // Delete sequence item + web::json::value remove_sequence_item(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& index = nmos::fields::nc::index(arguments); + + slog::log(gate, SLOG_FLF) << "Remove sequence item: " << property_id.serialize() << " index: " << index; + + // find the relevant nc_property_descriptor + const auto& property = find_property(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class); + if (!property.is_null()) + { + const auto& data = resource.data.at(nmos::fields::nc::name(property)); + + if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) + { + // property is not a sequence utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do RemoveSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do RemoveSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); } - // unknown property + if (data.as_array().size() > (size_t)index) + { + modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource) + { + auto& sequence = resource.data[nmos::fields::nc::name(property)].as_array(); + sequence.erase(index); + + }, make_propertry_changed_event(nmos::fields::nc::oid(resource.data), { { details::parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_removed, nc_id(index) } })); + + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); + } + + // out of bound utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do RemoveSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do RemoveSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); } - // Get sequence length - web::json::value get_sequence_length(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do RemoveSequenceItem"); + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + } + + // Get sequence length + web::json::value get_sequence_length(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - using web::json::value; + using web::json::value; - const auto& property_id = nmos::fields::nc::id(arguments); + const auto& property_id = nmos::fields::nc::id(arguments); - slog::log(gate, SLOG_FLF) << "Get sequence length: " << property_id.serialize(); + slog::log(gate, SLOG_FLF) << "Get sequence length: " << property_id.serialize(); - // find the relevant nc_property_descriptor - const auto& property = find_property(parse_nc_property_id(property_id), parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class); - if (!property.is_null()) + // find the relevant nc_property_descriptor + const auto& property = find_property(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class); + if (!property.is_null()) + { + if (!nmos::fields::nc::is_sequence(property)) { - if (!nmos::fields::nc::is_sequence(property)) - { - // property is not a sequence - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceLength"); - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); - } + // property is not a sequence + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceLength"); + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); + } - const auto& data = resource->data.at(nmos::fields::nc::name(property)); + const auto& data = resource.data.at(nmos::fields::nc::name(property)); - if (nmos::fields::nc::is_nullable(property)) + if (nmos::fields::nc::is_nullable(property)) + { + // can be null + if (data.is_null()) { - // can be null - if (data.is_null()) - { - // null - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, value::null()); - } + // null + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, value::null()); } - else + } + else + { + // cannot be null + if (data.is_null()) { - // cannot be null - if (data.is_null()) - { - // null - utility::stringstream_t ss; - ss << U("property: ") << property_id.serialize() << " is a null sequence to do GetSequenceLength"; - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); - } + // null + utility::stringstream_t ss; + ss << U("property: ") << property_id.serialize() << " is a null sequence to do GetSequenceLength"; + return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); } - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, uint32_t(data.as_array().size())); } - - // unknown property - utility::stringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << " to do GetSequenceLength"; - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, uint32_t(data.as_array().size())); } - // NcBlock methods implementation - // Gets descriptors of members of the block - web::json::value get_member_descriptors(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + // unknown property + utility::stringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << " to do GetSequenceLength"; + return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + } - using web::json::value; + // NcBlock methods implementation + // Gets descriptors of members of the block + web::json::value get_member_descriptors(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - const auto& recurse = nmos::fields::nc::recurse(arguments); // If recurse is set to true, nested members is to be retrieved + using web::json::value; - slog::log(gate, SLOG_FLF) << "Get descriptors of members of the block: " << "recurse: " << recurse; + const auto& recurse = nmos::fields::nc::recurse(arguments); // If recurse is set to true, nested members is to be retrieved - auto descriptors = value::array(); - nmos::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); + slog::log(gate, SLOG_FLF) << "Get descriptors of members of the block: " << "recurse: " << recurse; - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); - } + auto descriptors = value::array(); + nmos::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); - // Finds member(s) by path - web::json::value find_members_by_path(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); + } - using web::json::value; + // Finds member(s) by path + web::json::value find_members_by_path(nmos::resources& resources, const nmos::resource& resource_, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - // Relative path to search for (MUST not include the role of the block targeted by oid) - const auto& path = arguments.at(nmos::fields::nc::path); + using web::json::value; - slog::log(gate, SLOG_FLF) << "Find member(s) by path: " << "path: " << path.serialize(); + // Relative path to search for (MUST not include the role of the block targeted by oid) + const auto& path = arguments.at(nmos::fields::nc::path); - if (0 == path.size()) - { - // empty path - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty path to do FindMembersByPath")); - } + slog::log(gate, SLOG_FLF) << "Find member(s) by path: " << "path: " << path.serialize(); + + if (0 == path.size()) + { + // empty path + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty path to do FindMembersByPath")); + } - auto descriptors = value::array(); - value descriptor; + auto descriptors = value::array(); + value descriptor; + + nmos::resource resource = resource_; + for (const auto& role : path.as_array()) + { + // look for the role in members - for (const auto& role : path.as_array()) + if (resource.data.has_field(nmos::fields::nc::members)) { - // look for the role in members - if (resource->data.has_field(nmos::fields::nc::members)) + auto& members = nmos::fields::nc::members(resource.data); + auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& nc_block_member_descriptor) { - auto& members = nmos::fields::nc::members(resource->data); - auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& nc_block_member_descriptor) - { - return role.as_string() == nmos::fields::nc::role(nc_block_member_descriptor); - }); + return role.as_string() == nmos::fields::nc::role(nc_block_member_descriptor); + }); - if (members.end() != member_found) - { - descriptor = *member_found; + if (members.end() != member_found) + { + descriptor = *member_found; - // use oid to look for the next resource - resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(*member_found)))); - } - else - { - // no role - utility::stringstream_t ss; - ss << U("role: ") << role.as_string() << U(" not found to do FindMembersByPath"); - return make_control_protocol_error_response(handle, { nc_method_status::bad_oid }, ss.str()); - } + // use oid to look for the next resource + resource = *nmos::find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(*member_found)))); } else { - // no members - return make_control_protocol_error_response(handle, { nc_method_status::bad_oid }, U("no members to do FindMembersByPath")); + // no role + utility::stringstream_t ss; + ss << U("role: ") << role.as_string() << U(" not found to do FindMembersByPath"); + return make_control_protocol_error_response(handle, { nc_method_status::bad_oid }, ss.str()); } } - - web::json::push_back(descriptors, descriptor); - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); + else + { + // no members + return make_control_protocol_error_response(handle, { nc_method_status::bad_oid }, U("no members to do FindMembersByPath")); + } } - // Finds members with given role name or fragment - web::json::value find_members_by_role(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) - { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - - using web::json::value; - - const auto& role = nmos::fields::nc::role(arguments); // Role text to search for - const auto& case_sensitive = nmos::fields::nc::case_sensitive(arguments); // Signals if the comparison should be case sensitive - const auto& match_whole_string = nmos::fields::nc::match_whole_string(arguments); // TRUE to only return exact matches - const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks + web::json::push_back(descriptors, descriptor); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); + } - slog::log(gate, SLOG_FLF) << "Find members with given role name or fragment: " << "role: " << role; + // Finds members with given role name or fragment + web::json::value find_members_by_role(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - if (role.empty()) - { - // empty role - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty role to do FindMembersByRole")); - } + using web::json::value; - auto descriptors = value::array(); - nmos::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); + const auto& role = nmos::fields::nc::role(arguments); // Role text to search for + const auto& case_sensitive = nmos::fields::nc::case_sensitive(arguments); // Signals if the comparison should be case sensitive + const auto& match_whole_string = nmos::fields::nc::match_whole_string(arguments); // TRUE to only return exact matches + const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); - } + slog::log(gate, SLOG_FLF) << "Find members with given role name or fragment: " << "role: " << role; - // Finds members with given class id - web::json::value find_members_by_class_id(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + if (role.empty()) { - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + // empty role + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty role to do FindMembersByRole")); + } - using web::json::value; + auto descriptors = value::array(); + nmos::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); - const auto& class_id = parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for - const auto& include_derived = nmos::fields::nc::include_derived(arguments); // If TRUE it will also include derived class descriptors - const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); + } - slog::log(gate, SLOG_FLF) << "Find members with given class id: " << "class_id: " << nmos::details::make_nc_class_id(class_id).serialize(); + // Finds members with given class id + web::json::value find_members_by_class_id(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + { + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - if (class_id.empty()) - { - // empty class_id - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty classId to do FindMembersByClassId")); - } + using web::json::value; - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for + const auto& include_derived = nmos::fields::nc::include_derived(arguments); // If TRUE it will also include derived class descriptors + const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks - auto descriptors = value::array(); - nmos::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); + slog::log(gate, SLOG_FLF) << "Find members with given class id: " << "class_id: " << nmos::details::make_nc_class_id(class_id).serialize(); - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); + if (class_id.empty()) + { + // empty class_id + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty classId to do FindMembersByClassId")); } - // NcClassManager methods implementation - // Get a single class descriptor - web::json::value get_control_class(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) - { - using web::json::value; + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... - const auto& class_id = parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for - const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements + auto descriptors = value::array(); + nmos::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); +// auto descriptors = nmos::find_members_by_class_id(resources, resource, class_id, include_derived, recurse);// , descriptors.as_array()); - slog::log(gate, SLOG_FLF) << "Get a single class descriptor: " << "class_id: " << nmos::details::make_nc_class_id(class_id).serialize(); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); + } - if (class_id.empty()) - { - // empty class_id - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty classId to do GetControlClass")); - } + // NcClassManager methods implementation + // Get a single class descriptor + web::json::value get_control_class(nmos::resources&, const nmos::resource&, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + { + using web::json::value; - // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for + const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements - const auto& control_class = get_control_protocol_class(class_id); - if (!control_class.class_id.empty()) + slog::log(gate, SLOG_FLF) << "Get a single class descriptor: " << "class_id: " << nmos::details::make_nc_class_id(class_id).serialize(); + + if (class_id.empty()) + { + // empty class_id + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty classId to do GetControlClass")); + } + + // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... + + const auto& control_class = get_control_protocol_class(class_id); + if (!control_class.class_id.empty()) + { + auto& description = control_class.description; + auto& name = control_class.name; + auto& fixed_role = control_class.fixed_role; + auto properties = control_class.properties; + auto methods = value::array(); + for (const auto& method : control_class.methods) { web::json::push_back(methods, method.first); } + auto events = control_class.events; + + if (include_inherited) { - auto& description = control_class.description; - auto& name = control_class.name; - auto& fixed_role = control_class.fixed_role; - auto properties = control_class.properties; - auto methods = value::array(); - for (const auto& method : control_class.methods) { web::json::push_back(methods, method.first); } - auto events = control_class.events; + auto inherited_class_id = class_id; + inherited_class_id.pop_back(); - if (include_inherited) + while (!inherited_class_id.empty()) { - auto inherited_class_id = class_id; - inherited_class_id.pop_back(); - - while (!inherited_class_id.empty()) + const auto& inherited_control_class = get_control_protocol_class(inherited_class_id); { - const auto& inherited_control_class = get_control_protocol_class(inherited_class_id); - { - for (const auto& property : inherited_control_class.properties.as_array()) { web::json::push_back(properties, property); } - for (const auto& method : inherited_control_class.methods) { web::json::push_back(methods, method.first); } - for (const auto& event : inherited_control_class.events.as_array()) { web::json::push_back(events, event); } - } - inherited_class_id.pop_back(); + for (const auto& property : inherited_control_class.properties.as_array()) { web::json::push_back(properties, property); } + for (const auto& method : inherited_control_class.methods) { web::json::push_back(methods, method.first); } + for (const auto& event : inherited_control_class.events.as_array()) { web::json::push_back(events, event); } } + inherited_class_id.pop_back(); } - const auto descriptor = fixed_role.is_null() - ? details::make_nc_class_descriptor(description, class_id, name, properties, methods, events) - : details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), properties, methods, events); - - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptor); } + const auto descriptor = fixed_role.is_null() + ? details::make_nc_class_descriptor(description, class_id, name, properties, methods, events) + : details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), properties, methods, events); - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("classId not found")); + return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptor); } - // Get a single datatype descriptor - web::json::value get_datatype(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler get_control_protocol_datatype, control_protocol_property_changed_handler, slog::base_gate& gate) + return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("classId not found")); + } + + // Get a single datatype descriptor + web::json::value get_datatype(nmos::resources&, const nmos::resource&, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler get_control_protocol_datatype, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outter function, so access to control_protocol_resources is OK... @@ -625,5 +626,4 @@ namespace nmos return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("name not found")); } - } } diff --git a/Development/nmos/control_protocol_methods.h b/Development/nmos/control_protocol_methods.h index e686c702b..7d74c847a 100644 --- a/Development/nmos/control_protocol_methods.h +++ b/Development/nmos/control_protocol_methods.h @@ -11,40 +11,37 @@ namespace slog namespace nmos { - namespace details - { - // NcObject methods implementation - // Get property value - web::json::value get(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); - // Set property value - web::json::value set(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - // Get sequence item - web::json::value get_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); - // Set sequence item - web::json::value set_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - // Add item to sequence - web::json::value add_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - // Delete sequence item - web::json::value remove_sequence_item(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); - // Get sequence length - web::json::value get_sequence_length(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + // NcObject methods implementation + // Get property value + web::json::value get(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + // Set property value + web::json::value set(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + // Get sequence item + web::json::value get_sequence_item(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + // Set sequence item + web::json::value set_sequence_item(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + // Add item to sequence + web::json::value add_sequence_item(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + // Delete sequence item + web::json::value remove_sequence_item(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + // Get sequence length + web::json::value get_sequence_length(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); - // NcBlock methods implementation - // Get descriptors of members of the block - web::json::value get_member_descriptors(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); - // Finds member(s) by path - web::json::value find_members_by_path(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); - // Finds members with given role name or fragment - web::json::value find_members_by_role(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); - // Finds members with given class id - web::json::value find_members_by_class_id(nmos::resources& resources, nmos::resources::iterator resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + // NcBlock methods implementation + // Get descriptors of members of the block + web::json::value get_member_descriptors(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + // Finds member(s) by path + web::json::value find_members_by_path(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + // Finds members with given role name or fragment + web::json::value find_members_by_role(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + // Finds members with given class id + web::json::value find_members_by_class_id(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); - // NcClassManager methods implementation - // Get a single class descriptor - web::json::value get_control_class(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); - // Get a single datatype descriptor - web::json::value get_datatype(nmos::resources&, nmos::resources::iterator, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler get_control_protocol_datatype, control_protocol_property_changed_handler, slog::base_gate& gate); - } + // NcClassManager methods implementation + // Get a single class descriptor + web::json::value get_control_class(nmos::resources&, const nmos::resource&, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler get_control_protocol_class, get_control_protocol_datatype_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + // Get a single datatype descriptor + web::json::value get_datatype(nmos::resources&, const nmos::resource&, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_handler, get_control_protocol_datatype_handler get_control_protocol_datatype, control_protocol_property_changed_handler, slog::base_gate& gate); } #endif diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 3cc3af38a..153370a5e 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -120,13 +120,13 @@ namespace nmos to_methods_vector(make_nc_object_methods(), { // link NcObject method_ids with method functions - { nc_object_get_method_id, nmos::details::get }, - { nc_object_set_method_id, nmos::details::set }, - { nc_object_get_sequence_item_method_id, nmos::details::get_sequence_item }, - { nc_object_set_sequence_item_method_id, nmos::details::set_sequence_item }, - { nc_object_add_sequence_item_method_id, nmos::details::add_sequence_item }, - { nc_object_remove_sequence_item_method_id, nmos::details::remove_sequence_item }, - { nc_object_get_sequence_length_method_id, nmos::details::get_sequence_length } + { nc_object_get_method_id, get }, + { nc_object_set_method_id, set }, + { nc_object_get_sequence_item_method_id, get_sequence_item }, + { nc_object_set_sequence_item_method_id, set_sequence_item }, + { nc_object_add_sequence_item_method_id, add_sequence_item }, + { nc_object_remove_sequence_item_method_id, remove_sequence_item }, + { nc_object_get_sequence_length_method_id, get_sequence_length } }), // NcObject events to_vector(make_nc_object_events())) }, @@ -138,10 +138,10 @@ namespace nmos to_methods_vector(make_nc_block_methods(), { // link NcBlock method_ids with method functions - { nc_block_get_member_descriptors_method_id, nmos::details::get_member_descriptors }, - { nc_block_find_members_by_path_method_id, nmos::details::find_members_by_path }, - { nc_block_find_members_by_role_method_id, nmos::details::find_members_by_role }, - { nc_block_find_members_by_class_id_method_id, nmos::details::find_members_by_class_id } + { nc_block_get_member_descriptors_method_id, get_member_descriptors }, + { nc_block_find_members_by_path_method_id, find_members_by_path }, + { nc_block_find_members_by_role_method_id, find_members_by_role }, + { nc_block_find_members_by_class_id_method_id, find_members_by_class_id } }), // NcBlock events to_vector(make_nc_block_events())) }, @@ -177,8 +177,8 @@ namespace nmos to_methods_vector(make_nc_class_manager_methods(), { // link NcClassManager method_ids with method functions - { nc_class_manager_get_control_class_method_id, nmos::details::get_control_class }, - { nc_class_manager_get_datatype_method_id, nmos::details::get_datatype } + { nc_class_manager_get_control_class_method_id, get_control_class }, + { nc_class_manager_get_datatype_method_id, get_datatype } }), // NcClassManager events to_vector(make_nc_class_manager_events())) }, diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 1a0814530..f89d4b2cd 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -414,11 +414,11 @@ namespace nmos } // get block member descriptors - void get_member_descriptors(const resources& resources, resources::iterator resource, bool recurse, web::json::array& descriptors) + void get_member_descriptors(const resources& resources, const resource& resource, bool recurse, web::json::array& descriptors) { - if (resource->data.has_field(nmos::fields::nc::members)) + if (resource.data.has_field(nmos::fields::nc::members)) { - const auto& members = nmos::fields::nc::members(resource->data); + const auto& members = nmos::fields::nc::members(resource.data); for (const auto& member : members) { @@ -434,8 +434,11 @@ namespace nmos { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(member); - - get_member_descriptors(resources, find_resource(resources, utility::s2us(std::to_string(oid))), recurse, descriptors); + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + get_member_descriptors(resources, *found, recurse, descriptors); + } } } } @@ -443,7 +446,7 @@ namespace nmos } // find members with given role name or fragment - void find_members_by_role(const resources& resources, resources::iterator resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& descriptors) + void find_members_by_role(const resources& resources, const resource& resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& descriptors) { auto find_members_by_matching_role = [&](const web::json::array& members) { @@ -466,9 +469,9 @@ namespace nmos return boost::make_iterator_range(boost::make_filter_iterator(match, members.begin(), members.end()), boost::make_filter_iterator(match, members.end(), members.end())); }; - if (resource->data.has_field(nmos::fields::nc::members)) + if (resource.data.has_field(nmos::fields::nc::members)) { - const auto& members = nmos::fields::nc::members(resource->data); + const auto& members = nmos::fields::nc::members(resource.data); auto members_found = find_members_by_matching_role(members); for (const auto& member : members_found) @@ -485,8 +488,11 @@ namespace nmos { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(member); - - find_members_by_role(resources, find_resource(resources, utility::s2us(std::to_string(oid))), role, match_whole_string, case_sensitive, recurse, descriptors); + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + find_members_by_role(resources, *found, role, match_whole_string, case_sensitive, recurse, descriptors); + } } } } @@ -494,7 +500,7 @@ namespace nmos } // find members with given class id - void find_members_by_class_id(const resources& resources, resources::iterator resource, const nc_class_id& class_id_, bool include_derived, bool recurse, web::json::array& descriptors) + void find_members_by_class_id(const resources& resources, const nmos::resource& resource, const nc_class_id& class_id_, bool include_derived, bool recurse, web::json::array& descriptors) { auto find_members_by_matching_class_id = [&](const web::json::array& members) { @@ -511,9 +517,9 @@ namespace nmos return boost::make_iterator_range(boost::make_filter_iterator(match, members.begin(), members.end()), boost::make_filter_iterator(match, members.end(), members.end())); }; - if (resource->data.has_field(nmos::fields::nc::members)) + if (resource.data.has_field(nmos::fields::nc::members)) { - auto& members = nmos::fields::nc::members(resource->data); + auto& members = nmos::fields::nc::members(resource.data); auto members_found = find_members_by_matching_class_id(members); for (const auto& member : members_found) @@ -530,8 +536,11 @@ namespace nmos { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(member); - - find_members_by_class_id(resources, find_resource(resources, utility::s2us(std::to_string(oid))), class_id_, include_derived, recurse, descriptors); + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + find_members_by_class_id(resources, *found, class_id_, include_derived, recurse, descriptors); + } } } } diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index 9ec84379e..9402e19e9 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -60,13 +60,13 @@ namespace nmos web::json::value find_property(const nc_property_id& property_id, const nc_class_id& class_id, get_control_protocol_class_handler get_control_protocol_class); // get block memeber descriptors - void get_member_descriptors(const resources& resources, resources::iterator resource, bool recurse, web::json::array& descriptors); + void get_member_descriptors(const resources& resources, const resource& resource, bool recurse, web::json::array& descriptors); // find members with given role name or fragment - void find_members_by_role(const resources& resources, resources::iterator resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& nc_block_member_descriptors); + void find_members_by_role(const resources& resources, const resource& resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& nc_block_member_descriptors); // find members with given class id - void find_members_by_class_id(const resources& resources, resources::iterator resource, const nc_class_id& class_id, bool include_derived, bool recurse, web::json::array& descriptors); + void find_members_by_class_id(const resources& resources, const resource& resource, const nc_class_id& class_id, bool include_derived, bool recurse, web::json::array& descriptors); // push control protocol resource into other control protocol NcBlock resource void push_back(control_protocol_resource& nc_block_resource, const control_protocol_resource& resource); diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 890efe761..5f88ad66d 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -268,7 +268,7 @@ namespace nmos method_parameters_contraints_validation(arguments, method.first, get_control_protocol_datatype); // execute the relevant method handler, then accumulating up their response to reponses - response = method.second(resources, resource, handle, arguments, nmos::fields::nc::is_deprecated(method.first), get_control_protocol_class, get_control_protocol_datatype, property_changed, gate); + response = method.second(resources, *resource, handle, arguments, nmos::fields::nc::is_deprecated(method.first), get_control_protocol_class, get_control_protocol_datatype, property_changed, gate); } catch (const nmos::control_protocol_exception& e) { From 31321f7089dbe0e8028ec160bbbaa58a10a7da75 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 10 Jan 2024 12:56:47 +0000 Subject: [PATCH 086/250] Add ncp authorization field to IS-04 controls array of an NMOS Device --- Development/nmos/control_protocol_methods.cpp | 1 - Development/nmos/node_resources.cpp | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index bfa3125a7..b7d980a13 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -507,7 +507,6 @@ namespace nmos auto descriptors = value::array(); nmos::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); -// auto descriptors = nmos::find_members_by_class_id(resources, resource, class_id, include_derived, recurse);// , descriptors.as_array()); return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); } diff --git a/Development/nmos/node_resources.cpp b/Development/nmos/node_resources.cpp index 216ef7db2..2d6d8d232 100644 --- a/Development/nmos/node_resources.cpp +++ b/Development/nmos/node_resources.cpp @@ -145,7 +145,8 @@ namespace nmos { web::json::push_back(data[U("controls")], value_of({ { U("href"), ncp_uri.set_host(host).to_uri().to_string() }, - { U("type"), type } + { U("type"), type }, + { U("authorization"), nmos::experimental::fields::server_authorization(settings) } })); } } From ff3b9a3be4df68b0c3c56a570c495806abecc980 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 10 Jan 2024 13:14:17 +0000 Subject: [PATCH 087/250] Add IS-14 support --- Development/cmake/NmosCppLibraries.cmake | 3 + Development/nmos/api_utils.h | 2 + Development/nmos/configuration_api.cpp | 172 +++++++++++++++++++++++ Development/nmos/configuration_api.h | 20 +++ Development/nmos/is14_versions.h | 26 ++++ Development/nmos/node_resources.cpp | 22 +++ Development/nmos/node_server.cpp | 5 + Development/nmos/scope.h | 3 + Development/nmos/settings.cpp | 1 + Development/nmos/settings.h | 4 + 10 files changed, 258 insertions(+) create mode 100644 Development/nmos/configuration_api.cpp create mode 100644 Development/nmos/configuration_api.h create mode 100644 Development/nmos/is14_versions.h diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index b49f78736..ad02889df 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -921,6 +921,7 @@ set(NMOS_CPP_NMOS_SOURCES nmos/channels.cpp nmos/client_utils.cpp nmos/components.cpp + nmos/configuration_api.cpp nmos/connection_activation.cpp nmos/connection_api.cpp nmos/connection_events_activation.cpp @@ -1014,6 +1015,7 @@ set(NMOS_CPP_NMOS_HEADERS nmos/colorspace.h nmos/components.h nmos/copyable_atomic.h + nmos/configuration_api.h nmos/connection_activation.h nmos/connection_api.h nmos/connection_events_activation.h @@ -1048,6 +1050,7 @@ set(NMOS_CPP_NMOS_HEADERS nmos/is09_versions.h nmos/is10_versions.h nmos/is12_versions.h + nmos/is14_versions.h nmos/issuers.h nmos/json_fields.h nmos/json_schema.h diff --git a/Development/nmos/api_utils.h b/Development/nmos/api_utils.h index 19f0444f7..1b1bca1a5 100644 --- a/Development/nmos/api_utils.h +++ b/Development/nmos/api_utils.h @@ -57,6 +57,8 @@ namespace nmos const route_pattern channelmapping_api = make_route_pattern(U("api"), U("channelmapping")); // IS-09 System API (originally specified in JT-NM TR-1001-1:2018 Annex A) const route_pattern system_api = make_route_pattern(U("api"), U("system")); + // IS-14 Configuration API + const route_pattern configuration_api = make_route_pattern(U("api"), U("configuration")); // API version pattern const route_pattern version = make_route_pattern(U("version"), U("v[0-9]+\\.[0-9]+")); diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp new file mode 100644 index 000000000..b24770af7 --- /dev/null +++ b/Development/nmos/configuration_api.cpp @@ -0,0 +1,172 @@ +#include "nmos/configuration_api.h" + +//#include "cpprest/json_validator.h" +#include "nmos/api_utils.h" +#include "nmos/is14_versions.h" +//#include "nmos/json_schema.h" +#include "nmos/log_manip.h" +#include "nmos/model.h" + +namespace nmos +{ + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(nmos::node_model& model, slog::base_gate& gate); + + web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, slog::base_gate& gate) + { + using namespace web::http::experimental::listener::api_router_using_declarations; + + api_router configuration_api; + + configuration_api.support(U("/?"), methods::GET, [](http_request req, http_response res, const string_t&, const route_parameters&) + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("x-nmos/") }, req, res)); + return pplx::task_from_result(true); + }); + + configuration_api.support(U("/x-nmos/?"), methods::GET, [](http_request req, http_response res, const string_t&, const route_parameters&) + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("configuration/") }, req, res)); + return pplx::task_from_result(true); + }); + + if (validate_authorization) + { + configuration_api.support(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/?"), validate_authorization); + configuration_api.support(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/.*"), validate_authorization); + } + + const auto versions = with_read_lock(model.mutex, [&model] { return nmos::is14_versions::from_settings(model.settings); }); + configuration_api.support(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/?"), methods::GET, [versions](http_request req, http_response res, const string_t&, const route_parameters&) + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body(nmos::make_api_version_sub_routes(versions), req, res)); + return pplx::task_from_result(true); + }); + + configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, gate)); + + return configuration_api; + } + + namespace details + { + namespace fields + { + const web::json::field_as_string_or level{ U("level"), {} }; + const web::json::field_as_string_or index{ U("index"), {} }; + const web::json::field_as_string_or describe{ U("describe"), {} }; + } + + utility::string_t make_query_parameters(web::json::value flat_query_params) + { + // any non-string query parameters need serializing before encoding + + // all other string values need encoding + nmos::details::encode_elements(flat_query_params); + + return web::json::query_from_value(flat_query_params); + } + + web::json::value parse_query_parameters(const utility::string_t& query) + { + auto flat_query_params = web::json::value_from_query(query); + + // all other string values need decoding + nmos::details::decode_elements(flat_query_params); + + // any non-string query parameters need parsing after decoding... + if (flat_query_params.has_field(nmos::fields::nc::level)) + { + flat_query_params[nmos::fields::nc::level] = web::json::value::parse(nmos::details::fields::level(flat_query_params)); + } + if (flat_query_params.has_field(nmos::details::fields::index)) + { + flat_query_params[nmos::details::fields::index] = web::json::value::parse(nmos::details::fields::index(flat_query_params)); + } + if (flat_query_params.has_field(nmos::details::fields::describe)) + { + flat_query_params[nmos::details::fields::describe] = web::json::value::parse(nmos::details::fields::describe(flat_query_params)); + } + + return flat_query_params; + } + } + + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(nmos::node_model& model, slog::base_gate& gate_) + { + using namespace web::http::experimental::listener::api_router_using_declarations; + + api_router configuration_api; + + // check for supported API version + const auto versions = with_read_lock(model.mutex, [&model] { return nmos::is14_versions::from_settings(model.settings); }); + configuration_api.support(U(".*"), details::make_api_version_handler(versions, gate_)); + + configuration_api.support(U("/?"), methods::GET, [](http_request req, http_response res, const string_t&, const route_parameters&) + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("root/") }, req, res)); + return pplx::task_from_result(true); + }); + + configuration_api.mount(U("/root/?"), methods::GET, [](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) + { + using web::json::value; + using web::json::value_of; + + // extarct the role path + //auto role_path = req.relative_uri().path(); + + // extract and decode the query string + const auto flat_query_params = details::parse_query_parameters(req.request_uri().query()); + + value data; + + if (flat_query_params.has_integer_field(nmos::details::fields::level) && flat_query_params.has_integer_field(nmos::details::fields::index)) + { + if (flat_query_params.has_boolean_field(nmos::details::fields::describe)) + { + // Get datatype descriptor + // {baseUrl}/{rolePath}?level={propertyLevel}&index={propertyIndex}&describe=true + // see https://specs.amwa.tv/is-device-configuration/branches/publish-CR/docs/API_requests.html#getting-the-datatype-descriptor-of-a-property + data = value_of({ U("Get datatype descriptor") }); + } + else + { + // Get a property + // {baseUrl}/{rolePath}?level={propertyLevel}&index={propertyIndex} + // see https://specs.amwa.tv/is-device-configuration/branches/publish-CR/docs/API_requests.html#getting-a-property + data = value_of({ U("Get a property") }); + } + } + else + { + if (flat_query_params.has_boolean_field(nmos::details::fields::describe)) + { + // Get class descriptor + // {baseUrl}/{rolePath}?describe=true + // see https://specs.amwa.tv/is-device-configuration/branches/publish-CR/docs/API_requests.html#getting-the-class-descriptor-of-an-object + data = value_of({ U("Get class descriptor") }); + } + else + { + // Get block members + // {baseUrl}/{rolePath} + // see https://specs.amwa.tv/is-device-configuration/branches/publish-CR/docs/API_requests.html#getting-the-members-of-a-block + data = value_of({ U("Get block members") }); + } + } + + // parse role path + + set_reply(res, status_codes::OK, data); + return pplx::task_from_result(true); + }); + + //const web::json::experimental::json_validator validator + //{ + // nmos::experimental::load_json_schema, + // boost::copy_range>(is14_versions::all | boost::adaptors::transformed(experimental::make_systemapi_global_schema_uri)) + //}; + + return configuration_api; + } +} diff --git a/Development/nmos/configuration_api.h b/Development/nmos/configuration_api.h new file mode 100644 index 000000000..0028a9242 --- /dev/null +++ b/Development/nmos/configuration_api.h @@ -0,0 +1,20 @@ +#ifndef NMOS_CONFIGURATION_API_H +#define NMOS_CONFIGURATION_API_H + +#include "cpprest/api_router.h" + +namespace slog +{ + class base_gate; +} + +// Configuration API implementation +// See https://specs.amwa.tv/is-device-configuration/branches/publish-CR/docs/API_requests.html +namespace nmos +{ + struct node_model; + + web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, slog::base_gate& gate); +} + +#endif diff --git a/Development/nmos/is14_versions.h b/Development/nmos/is14_versions.h new file mode 100644 index 000000000..a5fc9d2c5 --- /dev/null +++ b/Development/nmos/is14_versions.h @@ -0,0 +1,26 @@ +#ifndef NMOS_IS14_VERSIONS_H +#define NMOS_IS14_VERSIONS_H + +#include +#include +#include "nmos/api_version.h" +#include "nmos/settings.h" + +namespace nmos +{ + namespace is14_versions + { + const api_version v1_0{ 1, 0 }; + + const std::set all{ nmos::is14_versions::v1_0 }; + + inline std::set from_settings(const nmos::settings& settings) + { + return settings.has_field(nmos::fields::is14_versions) + ? boost::copy_range>(nmos::fields::is14_versions(settings) | boost::adaptors::transformed([](const web::json::value& v) { return nmos::parse_api_version(v.as_string()); })) + : nmos::is14_versions::all; + } + } +} + +#endif diff --git a/Development/nmos/node_resources.cpp b/Development/nmos/node_resources.cpp index 2d6d8d232..623e7e614 100644 --- a/Development/nmos/node_resources.cpp +++ b/Development/nmos/node_resources.cpp @@ -17,6 +17,7 @@ #include "nmos/is07_versions.h" #include "nmos/is08_versions.h" #include "nmos/is12_versions.h" +#include "nmos/is14_versions.h" #include "nmos/media_type.h" #include "nmos/resource.h" #include "nmos/sdp_utils.h" // for nmos::make_components @@ -152,6 +153,27 @@ namespace nmos } } + if (0 <= nmos::fields::configuration_port(settings)) + { + for (const auto& version : nmos::is14_versions::from_settings(settings)) + { + auto configuration_uri = web::uri_builder() + .set_scheme(nmos::http_scheme(settings)) + .set_port(nmos::fields::connection_port(settings)) + .set_path(U("/x-nmos/configuration/") + make_api_version(version)); + auto type = U("urn:x-nmos:control:configuration/") + make_api_version(version); + + for (const auto& host : hosts) + { + web::json::push_back(data[U("controls")], value_of({ + { U("href"), configuration_uri.set_host(host).to_uri().to_string() }, + { U("type"), type }, + { U("authorization"), nmos::experimental::fields::server_authorization(settings) } + })); + } + } + } + return{ is04_versions::v1_3, types::device, std::move(data), false }; } diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index 5cb290d50..0c10d5920 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -3,6 +3,7 @@ #include "cpprest/ws_utils.h" #include "nmos/api_utils.h" #include "nmos/channelmapping_activation.h" +#include "nmos/configuration_api.h" #include "nmos/control_protocol_ws_api.h" #include "nmos/events_api.h" #include "nmos/events_ws_api.h" @@ -67,6 +68,10 @@ namespace nmos node_server.api_routers[{ {}, nmos::fields::channelmapping_port(node_model.settings) }].mount({}, nmos::make_channelmapping_api(node_model, node_implementation.validate_map, validate_authorization ? validate_authorization(nmos::experimental::scopes::channelmapping) : nullptr, gate)); + // Configure the Configuration API + + node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, gate)); + const auto& events_ws_port = nmos::fields::events_ws_port(node_model.settings); auto& events_ws_api = node_server.ws_handlers[{ {}, nmos::fields::events_ws_port(node_model.settings) }]; events_ws_api.first = nmos::make_events_ws_api(node_model, events_ws_api.second, node_implementation.ws_validate_authorization, gate); diff --git a/Development/nmos/scope.h b/Development/nmos/scope.h index 25d65004e..ee623e55f 100644 --- a/Development/nmos/scope.h +++ b/Development/nmos/scope.h @@ -26,6 +26,8 @@ namespace nmos const scope channelmapping{ U("channelmapping") }; // IS-12 const scope ncp{ U("ncp") }; + // IS-14 + const scope configuration{ U("configuration") }; } inline utility::string_t make_scope(const scope& scope) @@ -43,6 +45,7 @@ namespace nmos if (scopes::events.name == scope) { return scopes::events; } if (scopes::channelmapping.name == scope) { return scopes::channelmapping; } if (scopes::ncp.name == scope) { return scopes::ncp; } + if (scopes::configuration.name == scope) { return scopes::configuration; } return{}; } } diff --git a/Development/nmos/settings.cpp b/Development/nmos/settings.cpp index 5608fbcac..94251cb4d 100644 --- a/Development/nmos/settings.cpp +++ b/Development/nmos/settings.cpp @@ -86,6 +86,7 @@ namespace nmos web::json::insert(settings, std::make_pair(nmos::experimental::fields::authorization_redirect_port, http_port)); web::json::insert(settings, std::make_pair(nmos::experimental::fields::jwks_uri_port, http_port)); if (!registry) web::json::insert(settings, std::make_pair(nmos::fields::control_protocol_ws_port, ncp_ws_port)); + if (!registry) web::json::insert(settings, std::make_pair(nmos::fields::configuration_port, http_port)); } } } diff --git a/Development/nmos/settings.h b/Development/nmos/settings.h index 1134242c0..465e03594 100644 --- a/Development/nmos/settings.h +++ b/Development/nmos/settings.h @@ -107,6 +107,9 @@ namespace nmos // is12_versions [node]: used to specify the enabled API versions for a version-locked configuration const web::json::field_as_array is12_versions{ U("is12_versions") }; // when omitted, nmos::is12_versions::all is used + // is14_versions [node]: used to specify the enabled API versions for a version-locked configuration + const web::json::field_as_array is14_versions{ U("is14_versions") }; // when omitted, nmos::is14_versions::all is used + // pri [registry, node]: used for the 'pri' TXT record; specifying nmos::service_priorities::no_priority (maximum value) disables advertisement completely const web::json::field_as_integer_or pri{ U("pri"), 100 }; // default to highest_development_priority @@ -151,6 +154,7 @@ namespace nmos const web::json::field_as_integer_or system_port{ U("system_port"), 10641 }; // control_protocol_ws_port [node]: used to construct request URLs for the Control Protocol websocket, or negative to disable the control protocol features const web::json::field_as_integer_or control_protocol_ws_port{ U("control_protocol_ws_port"), 3218 }; + const web::json::field_as_integer_or configuration_port{ U("configuration_port"), 3219 }; // listen_backlog [registry, node]: the maximum length of the queue of pending connections, or zero for the implementation default (the implementation may not honour this value) const web::json::field_as_integer_or listen_backlog{ U("listen_backlog"), 0 }; From f6857293a30f3f61cd6a38a9ad37b7d51fff44b3 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 12 Mar 2024 14:10:51 +0000 Subject: [PATCH 088/250] Add IS-14 schemas --- Development/cmake/NmosCppLibraries.cmake | 80 +++++++++++++++++++ Development/nmos/is14_schemas/is14_schemas.h | 30 +++++++ Development/third_party/is-14/README.md | 9 +++ .../is-14/v1.0.x/APIs/schemas/base.json | 15 ++++ .../v1.0.x/APIs/schemas/descriptor-get.json | 6 ++ .../APIs/schemas/method-patch-request.json | 15 ++++ .../APIs/schemas/method-patch-response.json | 6 ++ .../v1.0.x/APIs/schemas/methods-base.json | 10 +++ .../is-14/v1.0.x/APIs/schemas/ms05-error.json | 6 ++ .../v1.0.x/APIs/schemas/properties-base.json | 10 +++ .../APIs/schemas/property-descriptor.json | 6 ++ .../APIs/schemas/property-value-get.json | 5 ++ .../schemas/property-value-put-request.json | 37 +++++++++ .../schemas/property-value-put-response.json | 6 ++ .../is-14/v1.0.x/APIs/schemas/property.json | 16 ++++ .../is-14/v1.0.x/APIs/schemas/rolePath.json | 17 ++++ .../v1.0.x/APIs/schemas/rolePaths-base.json | 10 +++ 17 files changed, 284 insertions(+) create mode 100644 Development/nmos/is14_schemas/is14_schemas.h create mode 100644 Development/third_party/is-14/README.md create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/base.json create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/descriptor-get.json create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-request.json create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-response.json create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/methods-base.json create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/ms05-error.json create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/properties-base.json create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/property-descriptor.json create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-get.json create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-put-request.json create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-put-response.json create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/property.json create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/rolePath.json create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/rolePaths-base.json diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index ad02889df..af1c3cc9f 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -834,6 +834,85 @@ target_include_directories(nmos_is12_schemas PUBLIC list(APPEND NMOS_CPP_TARGETS nmos_is12_schemas) add_library(nmos-cpp::nmos_is12_schemas ALIAS nmos_is12_schemas) +# nmos_is14_schemas library + +set(NMOS_IS14_SCHEMAS_HEADERS + nmos/is14_schemas/is14_schemas.h + ) + +set(NMOS_IS14_V1_0_TAG v1.0.x) + +set(NMOS_IS14_V1_0_SCHEMAS_JSON + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/base.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/descriptor-get.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/method-patch-request.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/method-patch-response.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/methods-base.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/ms05-error.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/properties-base.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/property.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/property-descriptor.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/property-value-get.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/property-value-put-request.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/property-value-put-response.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/rolePath.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/rolePaths-base.json + ) + +set(NMOS_IS14_SCHEMAS_JSON_MATCH "third_party/is-14/([^/]+)/APIs/schemas/([^;]+)\\.json") +set(NMOS_IS14_SCHEMAS_SOURCE_REPLACE "${CMAKE_CURRENT_BINARY_DIR_REPLACE}/nmos/is14_schemas/\\1/\\2.cpp") +string(REGEX REPLACE "${NMOS_IS14_SCHEMAS_JSON_MATCH}(;|$)" "${NMOS_IS14_SCHEMAS_SOURCE_REPLACE}\\3" NMOS_IS14_V1_0_SCHEMAS_SOURCES "${NMOS_IS14_V1_0_SCHEMAS_JSON}") + +foreach(JSON ${NMOS_IS14_V1_0_SCHEMAS_JSON}) + string(REGEX REPLACE "${NMOS_IS14_SCHEMAS_JSON_MATCH}" "${NMOS_IS14_SCHEMAS_SOURCE_REPLACE}" SOURCE "${JSON}") + string(REGEX REPLACE "${NMOS_IS14_SCHEMAS_JSON_MATCH}" "\\1" NS "${JSON}") + string(REGEX REPLACE "${NMOS_IS14_SCHEMAS_JSON_MATCH}" "\\2" VAR "${JSON}") + string(MAKE_C_IDENTIFIER "${NS}" NS) + string(MAKE_C_IDENTIFIER "${VAR}" VAR) + + file(WRITE "${SOURCE}.in" "\ +// Auto-generated from: ${JSON}\n\ +\n\ +namespace nmos\n\ +{\n\ + namespace is14_schemas\n\ + {\n\ + namespace ${NS}\n\ + {\n\ + const char* ${VAR} = R\"-auto-generated-(") + + file(READ "${JSON}" RAW) + file(APPEND "${SOURCE}.in" "${RAW}") + + file(APPEND "${SOURCE}.in" ")-auto-generated-\";\n\ + }\n\ + }\n\ +}\n") + + configure_file("${SOURCE}.in" "${SOURCE}" COPYONLY) +endforeach() + +add_library( + nmos_is14_schemas STATIC + ${NMOS_IS14_SCHEMAS_HEADERS} + ${NMOS_IS14_V1_0_SCHEMAS_SOURCES} + ) + +source_group("nmos\\is14_schemas\\Header Files" FILES ${NMOS_IS14_SCHEMAS_HEADERS}) +source_group("nmos\\is14_schemas\\${NMOS_IS14_V1_0_TAG}\\Source Files" FILES ${NMOS_IS14_V1_0_SCHEMAS_SOURCES}) + +target_link_libraries( + nmos_is14_schemas PRIVATE + nmos-cpp::compile-settings + ) +target_include_directories(nmos_is14_schemas PUBLIC + $ + $ + ) + +list(APPEND NMOS_CPP_TARGETS nmos_is14_schemas) +add_library(nmos-cpp::nmos_is14_schemas ALIAS nmos_is14_schemas) + # nmos-cpp library set(NMOS_CPP_BST_SOURCES @@ -1201,6 +1280,7 @@ target_link_libraries( nmos-cpp::nmos_is09_schemas nmos-cpp::nmos_is10_schemas nmos-cpp::nmos_is12_schemas + nmos-cpp::nmos_is14_schemas nmos-cpp::mdns nmos-cpp::slog nmos-cpp::OpenSSL diff --git a/Development/nmos/is14_schemas/is14_schemas.h b/Development/nmos/is14_schemas/is14_schemas.h new file mode 100644 index 000000000..952146919 --- /dev/null +++ b/Development/nmos/is14_schemas/is14_schemas.h @@ -0,0 +1,30 @@ +#ifndef NMOS_IS14_SCHEMAS_H +#define NMOS_IS14_SCHEMAS_H + +// Extern declarations for auto-generated constants +// could be auto-generated, but isn't currently! +namespace nmos +{ + namespace is14_schemas + { + namespace v1_0_x + { + extern const char* base; + extern const char* descriptor_get; + extern const char* method_patch_request; + extern const char* method_patch_response; + extern const char* methods_base; + extern const char* ms05_error; + extern const char* properties_base; + extern const char* property; + extern const char* property_descriptor; + extern const char* property_value_get; + extern const char* property_value_put_request; + extern const char* property_value_put_response; + extern const char* rolePath; + extern const char* rolePaths_base; + } + } +} + +#endif diff --git a/Development/third_party/is-14/README.md b/Development/third_party/is-14/README.md new file mode 100644 index 000000000..a6d9c931f --- /dev/null +++ b/Development/third_party/is-14/README.md @@ -0,0 +1,9 @@ +# AMWA IS-14 NMOS Device Configuration Specification + +This directory contains files from the [AMWA NMOS Device Configuration Specification](https://github.com/AMWA-TV/is-14), in particular tagged versions of the JSON schemas used by the API specifications. + +Original source code: + +- (c) AMWA 2024 +- Licensed under the Apache License, Version 2.0; http://www.apache.org/licenses/LICENSE-2.0 + diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/base.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/base.json new file mode 100644 index 000000000..d8c791bec --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/base.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Configuration API base resource", + "title": "Configuration API base resource", + "items": { + "type": "string", + "enum": [ + "rolePaths/" + ] + }, + "minItems": 1, + "maxItems": 1, + "uniqueItems": true +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/descriptor-get.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/descriptor-get.json new file mode 100644 index 000000000..28f76a40e --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/descriptor-get.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "NcMethodResultClassDescriptor", + "title": "NcMethodResultClassDescriptor" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-request.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-request.json new file mode 100644 index 000000000..42e253515 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-request.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "PATCH request body for invoking a method", + "title": "Invoke method body", + "required": [ + "arguments" + ], + "properties": { + "arguments": { + "type": "object", + "description": "Method arguments. Arguments are specified as nested properties inside this object and their types are dictated by the specific MS-05-02 model for the method targeted. For methods which do not have arguments defined the object MUST be an empty object." + } + } +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-response.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-response.json new file mode 100644 index 000000000..c74183f9a --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-response.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "NcMethodResult", + "title": "NcMethodResult" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/methods-base.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/methods-base.json new file mode 100644 index 000000000..dd30ab1c7 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/methods-base.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Configuration API /rolePaths/{rolePath}/methods base", + "title": "Configuration API /rolePaths/{rolePath}/methods base", + "items": { + "type": "string" + }, + "uniqueItems": true +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/ms05-error.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/ms05-error.json new file mode 100644 index 000000000..4eea45a37 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/ms05-error.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "NcMethodResultError", + "title": "NcMethodResultError" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/properties-base.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/properties-base.json new file mode 100644 index 000000000..ae1a93124 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/properties-base.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Configuration API /rolePaths/{rolePath}/properties base", + "title": "Configuration API /rolePaths/{rolePath}/properties base", + "items": { + "type": "string" + }, + "uniqueItems": true +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/property-descriptor.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/property-descriptor.json new file mode 100644 index 000000000..e564e0fd0 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/property-descriptor.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "NcMethodResultDatatypeDescriptor", + "title": "NcMethodResultDatatypeDescriptor" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-get.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-get.json new file mode 100644 index 000000000..0f018830a --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-get.json @@ -0,0 +1,5 @@ +{ + "type": "object", + "description": "NcMethodResultPropertyValue with the contents of the property", + "title": "NcMethodResultPropertyValue" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-put-request.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-put-request.json new file mode 100644 index 000000000..fdbd07d40 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-put-request.json @@ -0,0 +1,37 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "PUT request body for modyfing a property", + "title": "Modify property body", + "required": [ + "value" + ], + "properties": { + "value": { + "description": "New property value. The actual type is determined by the property's MS-05-02 datatype.", + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "integer" + }, + { + "type": "object" + }, + { + "type": "array" + }, + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + } +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-put-response.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-put-response.json new file mode 100644 index 000000000..c74183f9a --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/property-value-put-response.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "NcMethodResult", + "title": "NcMethodResult" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/property.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/property.json new file mode 100644 index 000000000..42cc45d09 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/property.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Configuration API /rolePaths/{rolePath}/properties/{propertyId}", + "title": "Configuration API /rolePaths/{rolePath}/properties/{propertyId}", + "items": { + "type": "string", + "enum": [ + "value/", + "descriptor/" + ] + }, + "minItems": 2, + "maxItems": 2, + "uniqueItems": true +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePath.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePath.json new file mode 100644 index 000000000..976cff6d7 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePath.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Configuration API /rolePaths/{rolePath}", + "title": "Configuration API /rolePaths/{rolePath}", + "items": { + "type": "string", + "enum": [ + "properties/", + "methods/", + "descriptors/" + ] + }, + "minItems": 3, + "maxItems": 3, + "uniqueItems": true +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePaths-base.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePaths-base.json new file mode 100644 index 000000000..de6d1c0bc --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePaths-base.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Configuration API /rolePaths base", + "title": "Configuration API /rolePaths base", + "items": { + "type": "string" + }, + "uniqueItems": true +} From 893f12d5d71c4352ea9181915cae6e40d062a2f2 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 13 Mar 2024 13:45:39 +0000 Subject: [PATCH 089/250] Add GET /rolePaths and /rolePaths/{rolePath} support --- Development/nmos/api_utils.h | 3 + Development/nmos/configuration_api.cpp | 158 ++++++++++++++++++------- 2 files changed, 117 insertions(+), 44 deletions(-) diff --git a/Development/nmos/api_utils.h b/Development/nmos/api_utils.h index c58e52064..3ca03b28f 100644 --- a/Development/nmos/api_utils.h +++ b/Development/nmos/api_utils.h @@ -89,6 +89,9 @@ namespace nmos const route_pattern outputSubroute = make_route_pattern(U("outputSubroute"), U("properties|sourceid|channels|caps")); const route_pattern activationId = make_route_pattern(U("activationId"), U("[a-zA-Z0-9\\-_]+")); + // Configuration API + const route_pattern rolePath = make_route_pattern(U("rolePath"), U("root|root\\.[a-zA-Z0-9\\-_\\.]+")); + // Common patterns const route_pattern resourceId = make_route_pattern(U("resourceId"), U("[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}")); } diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index b24770af7..8c0519d15 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -1,7 +1,11 @@ #include "nmos/configuration_api.h" +#include +#include //#include "cpprest/json_validator.h" #include "nmos/api_utils.h" +#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_utils.h" #include "nmos/is14_versions.h" //#include "nmos/json_schema.h" #include "nmos/log_manip.h" @@ -89,6 +93,71 @@ namespace nmos return flat_query_params; } + + void build_role_paths(const resources& resources, const nmos::resource& resource, const utility::string_t& base_role_path, std::set& role_paths) + { + if (resource.data.has_field(nmos::fields::nc::members)) + { + const auto& members = nmos::fields::nc::members(resource.data); + + for (const auto& member : members) + { + const auto role_path = base_role_path + U(".") + nmos::fields::nc::role(member); + role_paths.insert(role_path + U("/")); + + // get members on all NcBlock(s) + if (nmos::is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + { + // get resource based on the oid + const auto& oid = nmos::fields::nc::oid(member); + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + build_role_paths(resources, *found, role_path, role_paths); + } + } + } + } + } + + bool verify_role_path(const resources& resources, const nmos::resource& resource, std::list& role_path_segments) + { + if (resource.data.has_field(nmos::fields::nc::members)) + { + const auto& members = nmos::fields::nc::members(resource.data); + + const auto role_path_segement = role_path_segments.front(); + role_path_segments.pop_front(); + // find the role_path_segment member + auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& member) + { + return role_path_segement == nmos::fields::nc::role(member); + }); + + if (members.end() != member_found) + { + if (role_path_segments.empty()) + { + // role_path verified + return true; + } + + // get the role_path_segement member resource + if (is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(*member_found)))) + { + // get resource based on the oid + const auto& oid = nmos::fields::nc::oid(*member_found); + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + // verify the reminding role_path_segments + return verify_role_path(resources, *found, role_path_segments); + } + } + } + } + return false; + } } inline web::http::experimental::listener::api_router make_unmounted_configuration_api(nmos::node_model& model, slog::base_gate& gate_) @@ -103,70 +172,71 @@ namespace nmos configuration_api.support(U("/?"), methods::GET, [](http_request req, http_response res, const string_t&, const route_parameters&) { - set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("root/") }, req, res)); + set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("rolePaths/") }, req, res)); return pplx::task_from_result(true); }); - configuration_api.mount(U("/root/?"), methods::GET, [](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/?"), methods::GET, [&model](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) { using web::json::value; - using web::json::value_of; - // extarct the role path - //auto role_path = req.relative_uri().path(); + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + + std::set role_paths; + + // start at the root block resource + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); + if (resources.end() != resource) + { + // add root to role_paths + const auto role_path = nmos::fields::nc::role(resource->data); + role_paths.insert(role_path + U("/")); + + // add rest to the role_paths + details::build_role_paths(resources, *resource, role_path, role_paths); + } + + set_reply(res, status_codes::OK, nmos::make_sub_routes_body(role_paths, req, res)); + return pplx::task_from_result(true); + }); + + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/?"), methods::GET, [&model, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) + { + const string_t role_path = parameters.at(nmos::patterns::rolePath.name); - // extract and decode the query string - const auto flat_query_params = details::parse_query_parameters(req.request_uri().query()); + // tokenize the role_path with '.' delimiter + std::list role_path_segments; + boost::algorithm::split(role_path_segments, role_path, [](utility::char_t c) { return '.' == c; }); - value data; + bool result{ false }; - if (flat_query_params.has_integer_field(nmos::details::fields::level) && flat_query_params.has_integer_field(nmos::details::fields::index)) + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); + if (resources.end() != resource) { - if (flat_query_params.has_boolean_field(nmos::details::fields::describe)) - { - // Get datatype descriptor - // {baseUrl}/{rolePath}?level={propertyLevel}&index={propertyIndex}&describe=true - // see https://specs.amwa.tv/is-device-configuration/branches/publish-CR/docs/API_requests.html#getting-the-datatype-descriptor-of-a-property - data = value_of({ U("Get datatype descriptor") }); - } - else + const auto role = nmos::fields::nc::role(resource->data); + if (role_path_segments.size() && role == role_path_segments.front()) { - // Get a property - // {baseUrl}/{rolePath}?level={propertyLevel}&index={propertyIndex} - // see https://specs.amwa.tv/is-device-configuration/branches/publish-CR/docs/API_requests.html#getting-a-property - data = value_of({ U("Get a property") }); + role_path_segments.pop_front(); + + result = role_path_segments.size() ? details::verify_role_path(resources, *resource, role_path_segments) : true; } } + + if (result) + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("bulkProperties/"), U("descriptors/"), U("methods/"), U("properties/") }, req, res)); + } else { - if (flat_query_params.has_boolean_field(nmos::details::fields::describe)) - { - // Get class descriptor - // {baseUrl}/{rolePath}?describe=true - // see https://specs.amwa.tv/is-device-configuration/branches/publish-CR/docs/API_requests.html#getting-the-class-descriptor-of-an-object - data = value_of({ U("Get class descriptor") }); - } - else - { - // Get block members - // {baseUrl}/{rolePath} - // see https://specs.amwa.tv/is-device-configuration/branches/publish-CR/docs/API_requests.html#getting-the-members-of-a-block - data = value_of({ U("Get block members") }); - } + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); } - // parse role path - - set_reply(res, status_codes::OK, data); return pplx::task_from_result(true); }); - //const web::json::experimental::json_validator validator - //{ - // nmos::experimental::load_json_schema, - // boost::copy_range>(is14_versions::all | boost::adaptors::transformed(experimental::make_systemapi_global_schema_uri)) - //}; - return configuration_api; } } From a8794c5eb8b914b3bd7bd8c271096a8d440c8087 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 12 Apr 2024 09:21:21 +0100 Subject: [PATCH 090/250] Update comment --- Development/nmos/configuration_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 8c0519d15..3884525d3 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -205,7 +205,7 @@ namespace nmos { const string_t role_path = parameters.at(nmos::patterns::rolePath.name); - // tokenize the role_path with '.' delimiter + // tokenize the role_path with the '.' delimiter std::list role_path_segments; boost::algorithm::split(role_path_segments, role_path, [](utility::char_t c) { return '.' == c; }); From 05d62706e5f6d79e5562154ccdc00e9813f139be Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 12 Apr 2024 09:23:15 +0100 Subject: [PATCH 091/250] Update IS-14 schemas --- Development/cmake/NmosCppLibraries.cmake | 5 +++++ Development/nmos/is14_schemas/is14_schemas.h | 5 +++++ .../APIs/schemas/bulkProperties-get-response.json | 6 ++++++ .../APIs/schemas/bulkProperties-set-request.json | 15 +++++++++++++++ .../APIs/schemas/bulkProperties-set-response.json | 6 ++++++ .../schemas/bulkProperties-validate-request.json | 15 +++++++++++++++ .../schemas/bulkProperties-validate-response.json | 6 ++++++ .../is-14/v1.0.x/APIs/schemas/property.json | 4 ++-- .../is-14/v1.0.x/APIs/schemas/rolePath.json | 9 +++++---- 9 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-get-response.json create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-request.json create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-response.json create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-request.json create mode 100644 Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-response.json diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index af1c3cc9f..12ce63b2c 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -844,6 +844,11 @@ set(NMOS_IS14_V1_0_TAG v1.0.x) set(NMOS_IS14_V1_0_SCHEMAS_JSON third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/base.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-get-response.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-set-request.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-set-response.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-validate-request.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-validate-response.json third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/descriptor-get.json third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/method-patch-request.json third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/method-patch-response.json diff --git a/Development/nmos/is14_schemas/is14_schemas.h b/Development/nmos/is14_schemas/is14_schemas.h index 952146919..2c4e5f123 100644 --- a/Development/nmos/is14_schemas/is14_schemas.h +++ b/Development/nmos/is14_schemas/is14_schemas.h @@ -10,6 +10,11 @@ namespace nmos namespace v1_0_x { extern const char* base; + extern const char* bulkProperties_get_response; + extern const char* bulkProperties_set_request; + extern const char* bulkProperties_set_response; + extern const char* bulkProperties_validate_request; + extern const char* bulkProperties_validate_response; extern const char* descriptor_get; extern const char* method_patch_request; extern const char* method_patch_response; diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-get-response.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-get-response.json new file mode 100644 index 000000000..9ddc05ead --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-get-response.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Returns a NcMethodResultBulkValuesHolder from a bulkProperties GET", + "title": "NcMethodResultBulkValuesHolder" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-request.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-request.json new file mode 100644 index 000000000..04c69a0e4 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-request.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "PUT request body for invoking SetPropertiesByPaths method on NcBulkPropertiesManager", + "title": "SetPropertiesByPaths", + "required": [ + "arguments" + ], + "properties": { + "arguments": { + "type": "object", + "description": "Method arguments. Arguments are specified as nested properties inside this object and their types are dictated by the specific MS-05-02 model for the method targeted. Arguments only need to be included for methods which have arguments and MUST be omitted if the method does not require any arguments." + } + } +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-response.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-response.json new file mode 100644 index 000000000..6b5885c01 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-response.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Returns a NcMethodResultObjectPropertiesSetValidation from a bulkProperties PUT", + "title": "NcMethodResultObjectPropertiesSetValidation" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-request.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-request.json new file mode 100644 index 000000000..a645997ff --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-request.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "PATCH request body for validating NcBulkValuesHolder object.", + "title": "ValidateSetPropertiesByPaths", + "required": [ + "arguments" + ], + "properties": { + "arguments": { + "type": "object", + "description": "Method arguments. Arguments are specified as nested properties inside this object and their types are dictated by the specific MS-05-02 model for the method targeted. Arguments only need to be included for methods which have arguments and MUST be omitted if the method does not require any arguments." + } + } +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-response.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-response.json new file mode 100644 index 000000000..0a077c2f3 --- /dev/null +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-response.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Returns a NcMethodResultObjectPropertiesSetValidation from a bulkProperties OPTIONS", + "title": "NcMethodResultObjectPropertiesSetValidation" +} diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/property.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/property.json index 42cc45d09..0a53e5103 100644 --- a/Development/third_party/is-14/v1.0.x/APIs/schemas/property.json +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/property.json @@ -6,8 +6,8 @@ "items": { "type": "string", "enum": [ - "value/", - "descriptor/" + "descriptor/", + "value/" ] }, "minItems": 2, diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePath.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePath.json index 976cff6d7..0cafe9c28 100644 --- a/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePath.json +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePath.json @@ -6,12 +6,13 @@ "items": { "type": "string", "enum": [ - "properties/", + "bulkProperties", + "descriptors/", "methods/", - "descriptors/" + "properties/" ] }, - "minItems": 3, - "maxItems": 3, + "minItems": 4, + "maxItems": 4, "uniqueItems": true } From 3a63684f0f6d79c1967e90b46c8693333b29470a Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 12 Apr 2024 19:30:13 +0100 Subject: [PATCH 092/250] Remove tabs with spaces --- Development/nmos/is14_schemas/is14_schemas.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Development/nmos/is14_schemas/is14_schemas.h b/Development/nmos/is14_schemas/is14_schemas.h index 2c4e5f123..7a261fc04 100644 --- a/Development/nmos/is14_schemas/is14_schemas.h +++ b/Development/nmos/is14_schemas/is14_schemas.h @@ -23,11 +23,11 @@ namespace nmos extern const char* properties_base; extern const char* property; extern const char* property_descriptor; - extern const char* property_value_get; - extern const char* property_value_put_request; - extern const char* property_value_put_response; - extern const char* rolePath; - extern const char* rolePaths_base; + extern const char* property_value_get; + extern const char* property_value_put_request; + extern const char* property_value_put_response; + extern const char* rolePath; + extern const char* rolePaths_base; } } } From c8eb564debf2ba989629dbbcbfd810e36f7013ea Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe <64410119+jonathan-r-thorpe@users.noreply.github.com> Date: Mon, 22 Apr 2024 15:06:25 +0100 Subject: [PATCH 093/250] IS-14 Added "/rolePaths/{rolePath}/properties" Endpoint (#1) Added /rolePaths/{rolePath}/properties endpoint Co-authored-by: Simon Lo --- Development/nmos/configuration_api.cpp | 92 ++++++++++++++++++++++---- Development/nmos/configuration_api.h | 3 +- Development/nmos/node_server.cpp | 2 +- 3 files changed, 83 insertions(+), 14 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 3884525d3..c1050dfc5 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -5,6 +5,7 @@ //#include "cpprest/json_validator.h" #include "nmos/api_utils.h" #include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_state.h" #include "nmos/control_protocol_utils.h" #include "nmos/is14_versions.h" //#include "nmos/json_schema.h" @@ -13,9 +14,9 @@ namespace nmos { - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(nmos::node_model& model, slog::base_gate& gate); + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(nmos::node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, slog::base_gate& gate) + web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -46,7 +47,7 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, gate)); + configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, gate)); return configuration_api; } @@ -120,11 +121,11 @@ namespace nmos } } - bool verify_role_path(const resources& resources, const nmos::resource& resource, std::list& role_path_segments) + web::json::value get_child_nc_object(const resources& resources, const nmos::resource& parent_nc_block_resource, std::list& role_path_segments) { - if (resource.data.has_field(nmos::fields::nc::members)) + if (parent_nc_block_resource.data.has_field(nmos::fields::nc::members)) { - const auto& members = nmos::fields::nc::members(resource.data); + const auto& members = nmos::fields::nc::members(parent_nc_block_resource.data); const auto role_path_segement = role_path_segments.front(); role_path_segments.pop_front(); @@ -139,28 +140,28 @@ namespace nmos if (role_path_segments.empty()) { // role_path verified - return true; + return *member_found; } // get the role_path_segement member resource if (is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(*member_found)))) { // get resource based on the oid - const auto& oid = nmos::fields::nc::oid(*member_found); + const auto& oid = nmos::fields::nc::oid(*member_found); const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != found) { // verify the reminding role_path_segments - return verify_role_path(resources, *found, role_path_segments); + return get_child_nc_object(resources, *found, role_path_segments); } } } } - return false; + return web::json::value{}; } } - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(nmos::node_model& model, slog::base_gate& gate_) + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(nmos::node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate_) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -221,7 +222,7 @@ namespace nmos { role_path_segments.pop_front(); - result = role_path_segments.size() ? details::verify_role_path(resources, *resource, role_path_segments) : true; + result = role_path_segments.size() ? !details::get_child_nc_object(resources, *resource, role_path_segments).is_null() : true; } } @@ -237,6 +238,73 @@ namespace nmos return pplx::task_from_result(true); }); + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) + { + const string_t role_path = parameters.at(nmos::patterns::rolePath.name); + + // tokenize the role_path with the '.' delimiter + std::list role_path_segments; + boost::algorithm::split(role_path_segments, role_path, [](utility::char_t c) { return '.' == c; }); + + bool result{ false }; + std::set properties_routes; + + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); + if (resources.end() != resource) + { + const auto role = nmos::fields::nc::role(resource->data); + if (role_path_segments.size() && role == role_path_segments.front()) + { + role_path_segments.pop_front(); + + auto nc_object = role_path_segments.size() ? details::get_child_nc_object(resources, *resource, role_path_segments) : resource->data; + + result = !nc_object.is_null(); + + if (result) + { + nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)); + + while (!class_id.empty()) + { + const auto& control_class = get_control_protocol_class_descriptor(class_id); + auto& property_descriptors = control_class.property_descriptors.as_array(); + + auto properties_route = boost::copy_range>(property_descriptors | boost::adaptors::transformed([](const web::json::value& property_descriptor) + { + auto make_property_id = [](const web::json::value& property_descriptor) + { + auto property_id = nmos::fields::nc::id(property_descriptor); + utility::ostringstream_t os; + os << nmos::fields::nc::level(property_id) << 'p' << nmos::fields::nc::index(property_id); + return os.str(); + }; + + return make_property_id(property_descriptor) + U("/"); + })); + + properties_routes.insert(properties_route.begin(), properties_route.end()); + + class_id.pop_back(); + } + } + } + } + + if (result) + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body(properties_routes, req, res)); + } + else + { + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return pplx::task_from_result(true); + }); + return configuration_api; } } diff --git a/Development/nmos/configuration_api.h b/Development/nmos/configuration_api.h index 0028a9242..8c6795b40 100644 --- a/Development/nmos/configuration_api.h +++ b/Development/nmos/configuration_api.h @@ -2,6 +2,7 @@ #define NMOS_CONFIGURATION_API_H #include "cpprest/api_router.h" +#include "nmos/control_protocol_handlers.h" namespace slog { @@ -14,7 +15,7 @@ namespace nmos { struct node_model; - web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, slog::base_gate& gate); + web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); } #endif diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index 56882521c..710b31d95 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -77,7 +77,7 @@ namespace nmos // Configure the Configuration API - node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, gate)); + node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, gate)); const auto& events_ws_port = nmos::fields::events_ws_port(node_model.settings); auto& events_ws_api = node_server.ws_handlers[{ {}, nmos::fields::events_ws_port(node_model.settings) }]; From b4a903810f485259e21e9721445fea2a1e4986a8 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 23 Apr 2024 18:32:19 +0100 Subject: [PATCH 094/250] Add const root block role --- Development/nmos/control_protocol_resources.cpp | 2 +- Development/nmos/control_protocol_typedefs.h | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index 0fde54c33..26030a6f1 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -32,7 +32,7 @@ namespace nmos { using web::json::value; - return details::make_block(1, value::null(), U("root"), U("Root"), U("Root block"), value::null(), value::null(), value::array()); + return details::make_block(nmos::root_block_oid, value::null(), nmos::root_block_role, U("Root"), U("Root block"), value::null(), value::null(), value::array()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index 7afcefedb..541e868ca 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -232,8 +232,6 @@ namespace nmos // NcOid // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncoid typedef uint32_t nc_oid; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Blocks.html - const nc_oid root_block_oid{ 1 }; // NcUri // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncuri @@ -381,6 +379,11 @@ namespace nmos friend bool operator!=(const nc_touchpoint_resource_nmos_channel_mapping& lhs, const nc_touchpoint_resource_nmos_channel_mapping& rhs) { return !(lhs == rhs); } friend bool operator<(const nc_touchpoint_resource_nmos_channel_mapping& lhs, const nc_touchpoint_resource_nmos_channel_mapping& rhs) { return lhs.tied() < rhs.tied(); } }; + + // Root block specification + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Blocks.html + const nc_oid root_block_oid{ 1 }; + const utility::string_t root_block_role{ U("root") }; } #endif From a328772312782dfc8d411a14ab5378fd66efad0b Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 24 Apr 2024 18:26:11 +0100 Subject: [PATCH 095/250] Remove blanks --- Development/nmos/configuration_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index c1050dfc5..7e0a4921a 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -292,7 +292,7 @@ namespace nmos } } } - + if (result) { set_reply(res, status_codes::OK, nmos::make_sub_routes_body(properties_routes, req, res)); From 143f4a057c808f0c70665a50eb6a08442d16a4e1 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 24 Apr 2024 18:29:39 +0100 Subject: [PATCH 096/250] Add /rolePaths/{rolePath}/methods endpoint --- Development/nmos/configuration_api.cpp | 70 ++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 7e0a4921a..970e9b80c 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -305,6 +305,76 @@ namespace nmos return pplx::task_from_result(true); }); + + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/methods/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) + { + const string_t role_path = parameters.at(nmos::patterns::rolePath.name); + + // tokenize the role_path with the '.' delimiter + std::list role_path_segments; + boost::algorithm::split(role_path_segments, role_path, [](utility::char_t c) { return '.' == c; }); + + bool result{ false }; + std::set methods_routes; + + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); + if (resources.end() != resource) + { + const auto role = nmos::fields::nc::role(resource->data); + if (role_path_segments.size() && role == role_path_segments.front()) + { + role_path_segments.pop_front(); + + auto nc_object = role_path_segments.size() ? details::get_child_nc_object(resources, *resource, role_path_segments) : resource->data; + + result = !nc_object.is_null(); + + if (result) + { + nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)); + + while (!class_id.empty()) + { + const auto& control_class = get_control_protocol_class_descriptor(class_id); + auto& method_descriptors = control_class.method_descriptors; + + auto methods_route = boost::copy_range>(method_descriptors | boost::adaptors::transformed([](const nmos::experimental::method& method) + { + auto make_method_id = [](const nmos::experimental::method& method) + { + // method tuple definition described in control_protocol_handlers.h + auto& nc_method_descriptor = std::get<0>(method); + auto method_id = nmos::fields::nc::id(nc_method_descriptor); + utility::ostringstream_t os; + os << nmos::fields::nc::level(method_id) << 'm' << nmos::fields::nc::index(method_id); + return os.str(); + }; + + return make_method_id(method) + U("/"); + })); + + methods_routes.insert(methods_route.begin(), methods_route.end()); + + class_id.pop_back(); + } + } + } + } + + if (result) + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body(methods_routes, req, res)); + } + else + { + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return pplx::task_from_result(true); + }); + return configuration_api; } } From 96c53207623a38a70fe5697dd1a8f62fd758b0c8 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 25 Apr 2024 23:25:00 +0100 Subject: [PATCH 097/250] Remove blank --- Development/nmos-cpp-node/node_implementation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 1e89fb8e0..bba973805 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1113,7 +1113,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // helper function to create Example control instance auto make_example_control = [&](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const value& touchpoints, const value& runtime_property_constraints, // level 2: runtime constraints. See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use of make_nc_property_constraints_string and make_nc_property_constraints_number to create runtime constraints + // use of make_nc_property_constraints_string and make_nc_property_constraints_number to create runtime constraints example_enum enum_property_, const utility::string_t& string_property_, uint64_t number_property_, From d05ce0238aeccf5173d7a85f5d1771fd53691b51 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 25 Apr 2024 23:30:13 +0100 Subject: [PATCH 098/250] Add /rolePaths/{rolePath}/descriptor endpoint, and tidy up --- Development/nmos/configuration_api.cpp | 251 ++++++++++++++----------- 1 file changed, 143 insertions(+), 108 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 970e9b80c..1b92e864f 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -121,7 +121,7 @@ namespace nmos } } - web::json::value get_child_nc_object(const resources& resources, const nmos::resource& parent_nc_block_resource, std::list& role_path_segments) + web::json::value get_nc_block_member_descriptor(const resources& resources, const nmos::resource& parent_nc_block_resource, std::list& role_path_segments) { if (parent_nc_block_resource.data.has_field(nmos::fields::nc::members)) { @@ -139,7 +139,7 @@ namespace nmos { if (role_path_segments.empty()) { - // role_path verified + // NcBlockMemberDescriptor return *member_found; } @@ -147,18 +147,58 @@ namespace nmos if (is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(*member_found)))) { // get resource based on the oid - const auto& oid = nmos::fields::nc::oid(*member_found); + const auto& oid = nmos::fields::nc::oid(*member_found); const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != found) { - // verify the reminding role_path_segments - return get_child_nc_object(resources, *found, role_path_segments); + return get_nc_block_member_descriptor(resources, *found, role_path_segments); } } } } return web::json::value{}; } + + web::json::value get_nc_object(const resources& resources, std::list& role_path_segments) + { + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); + if (resources.end() != resource) + { + const auto role = nmos::fields::nc::role(resource->data); + if (role_path_segments.size() && role == role_path_segments.front()) + { + role_path_segments.pop_front(); + + if (role_path_segments.size()) + { + const auto& block_member_descriptor = details::get_nc_block_member_descriptor(resources, *resource, role_path_segments); + if (!block_member_descriptor.is_null()) + { + const auto& oid = nmos::fields::nc::oid(block_member_descriptor); + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + return found->data; + } + } + } + else + { + return resource->data; + } + } + } + return web::json::value{}; + } + + web::json::value get_nc_object(const resources& resources, const utility::string_t& role_path) + { + // tokenize the role_path with the '.' delimiter + std::list role_path_segments; + boost::algorithm::split(role_path_segments, role_path, [](utility::char_t c) { return '.' == c; }); + + return get_nc_object(resources, role_path_segments); + } } inline web::http::experimental::listener::api_router make_unmounted_configuration_api(nmos::node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate_) @@ -206,29 +246,12 @@ namespace nmos { const string_t role_path = parameters.at(nmos::patterns::rolePath.name); - // tokenize the role_path with the '.' delimiter - std::list role_path_segments; - boost::algorithm::split(role_path_segments, role_path, [](utility::char_t c) { return '.' == c; }); - - bool result{ false }; - auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); - if (resources.end() != resource) - { - const auto role = nmos::fields::nc::role(resource->data); - if (role_path_segments.size() && role == role_path_segments.front()) - { - role_path_segments.pop_front(); - result = role_path_segments.size() ? !details::get_child_nc_object(resources, *resource, role_path_segments).is_null() : true; - } - } - - if (result) + if (!details::get_nc_object(resources, role_path).is_null()) { - set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("bulkProperties/"), U("descriptors/"), U("methods/"), U("properties/") }, req, res)); + set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("bulkProperties/"), U("descriptor/"), U("methods/"), U("properties/") }, req, res)); } else { @@ -242,59 +265,38 @@ namespace nmos { const string_t role_path = parameters.at(nmos::patterns::rolePath.name); - // tokenize the role_path with the '.' delimiter - std::list role_path_segments; - boost::algorithm::split(role_path_segments, role_path, [](utility::char_t c) { return '.' == c; }); - - bool result{ false }; - std::set properties_routes; - auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); - if (resources.end() != resource) - { - const auto role = nmos::fields::nc::role(resource->data); - if (role_path_segments.size() && role == role_path_segments.front()) - { - role_path_segments.pop_front(); - auto nc_object = role_path_segments.size() ? details::get_child_nc_object(resources, *resource, role_path_segments) : resource->data; + const auto& nc_object = details::get_nc_object(resources, role_path); + if (!nc_object.is_null()) + { + std::set properties_routes; - result = !nc_object.is_null(); + auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)); + while (!class_id.empty()) + { + const auto& control_class = get_control_protocol_class_descriptor(class_id); + auto& property_descriptors = control_class.property_descriptors.as_array(); - if (result) + auto properties_route = boost::copy_range>(property_descriptors | boost::adaptors::transformed([](const web::json::value& property_descriptor) { - nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)); - - while (!class_id.empty()) + auto make_property_id = [](const web::json::value& property_descriptor) { - const auto& control_class = get_control_protocol_class_descriptor(class_id); - auto& property_descriptors = control_class.property_descriptors.as_array(); + auto property_id = nmos::fields::nc::id(property_descriptor); + utility::ostringstream_t os; + os << nmos::fields::nc::level(property_id) << 'p' << nmos::fields::nc::index(property_id); + return os.str(); + }; - auto properties_route = boost::copy_range>(property_descriptors | boost::adaptors::transformed([](const web::json::value& property_descriptor) - { - auto make_property_id = [](const web::json::value& property_descriptor) - { - auto property_id = nmos::fields::nc::id(property_descriptor); - utility::ostringstream_t os; - os << nmos::fields::nc::level(property_id) << 'p' << nmos::fields::nc::index(property_id); - return os.str(); - }; + return make_property_id(property_descriptor) + U("/"); + })); - return make_property_id(property_descriptor) + U("/"); - })); + properties_routes.insert(properties_route.begin(), properties_route.end()); - properties_routes.insert(properties_route.begin(), properties_route.end()); - - class_id.pop_back(); - } - } + class_id.pop_back(); } - } - if (result) - { set_reply(res, status_codes::OK, nmos::make_sub_routes_body(properties_routes, req, res)); } else @@ -305,67 +307,100 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/methods/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) { const string_t role_path = parameters.at(nmos::patterns::rolePath.name); - // tokenize the role_path with the '.' delimiter - std::list role_path_segments; - boost::algorithm::split(role_path_segments, role_path, [](utility::char_t c) { return '.' == c; }); + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + + const auto& nc_object = details::get_nc_object(resources, role_path); + if (!nc_object.is_null()) + { + std::set methods_routes; + + nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)); + while (!class_id.empty()) + { + const auto& control_class = get_control_protocol_class_descriptor(class_id); + auto& method_descriptors = control_class.method_descriptors; + + auto methods_route = boost::copy_range>(method_descriptors | boost::adaptors::transformed([](const nmos::experimental::method& method) + { + auto make_method_id = [](const nmos::experimental::method& method) + { + // method tuple definition described in control_protocol_handlers.h + auto& nc_method_descriptor = std::get<0>(method); + auto method_id = nmos::fields::nc::id(nc_method_descriptor); + utility::ostringstream_t os; + os << nmos::fields::nc::level(method_id) << 'm' << nmos::fields::nc::index(method_id); + return os.str(); + }; + + return make_method_id(method) + U("/"); + })); - bool result{ false }; - std::set methods_routes; + methods_routes.insert(methods_route.begin(), methods_route.end()); + + class_id.pop_back(); + } + + set_reply(res, status_codes::OK, nmos::make_sub_routes_body(methods_routes, req, res)); + } + else + { + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return pplx::task_from_result(true); + }); + + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/descriptor/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) + { + using web::json::value_from_elements; + + const string_t role_path = parameters.at(nmos::patterns::rolePath.name); auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); - if (resources.end() != resource) + + const auto& nc_object = details::get_nc_object(resources, role_path); + if (!nc_object.is_null()) { - const auto role = nmos::fields::nc::role(resource->data); - if (role_path_segments.size() && role == role_path_segments.front()) + nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)); + + if (!class_id.empty()) { - role_path_segments.pop_front(); + const auto& control_class = get_control_protocol_class_descriptor(class_id); - auto nc_object = role_path_segments.size() ? details::get_child_nc_object(resources, *resource, role_path_segments) : resource->data; + auto& description = control_class.description; + auto& name = control_class.name; + auto& fixed_role = control_class.fixed_role; + auto property_descriptors = control_class.property_descriptors; + auto method_descriptors = value::array(); + for (const auto& method_descriptor : control_class.method_descriptors) { web::json::push_back(method_descriptors, std::get<0>(method_descriptor)); } + auto event_descriptors = control_class.event_descriptors; - result = !nc_object.is_null(); + auto inherited_class_id = class_id; + inherited_class_id.pop_back(); - if (result) + while (!inherited_class_id.empty()) { - nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)); - - while (!class_id.empty()) + const auto& inherited_control_class = get_control_protocol_class_descriptor(inherited_class_id); { - const auto& control_class = get_control_protocol_class_descriptor(class_id); - auto& method_descriptors = control_class.method_descriptors; - - auto methods_route = boost::copy_range>(method_descriptors | boost::adaptors::transformed([](const nmos::experimental::method& method) - { - auto make_method_id = [](const nmos::experimental::method& method) - { - // method tuple definition described in control_protocol_handlers.h - auto& nc_method_descriptor = std::get<0>(method); - auto method_id = nmos::fields::nc::id(nc_method_descriptor); - utility::ostringstream_t os; - os << nmos::fields::nc::level(method_id) << 'm' << nmos::fields::nc::index(method_id); - return os.str(); - }; - - return make_method_id(method) + U("/"); - })); - - methods_routes.insert(methods_route.begin(), methods_route.end()); - - class_id.pop_back(); + for (const auto& property_descriptor : inherited_control_class.property_descriptors.as_array()) { web::json::push_back(property_descriptors, property_descriptor); } + for (const auto& method_descriptor : inherited_control_class.method_descriptors) { web::json::push_back(method_descriptors, std::get<0>(method_descriptor)); } + for (const auto& event_descriptor : inherited_control_class.event_descriptors.as_array()) { web::json::push_back(event_descriptors, event_descriptor); } } + inherited_class_id.pop_back(); } - } - } - if (result) - { - set_reply(res, status_codes::OK, nmos::make_sub_routes_body(methods_routes, req, res)); + auto class_descriptor = fixed_role.is_null() + ? details::make_nc_class_descriptor(description, class_id, name, property_descriptors, method_descriptors, event_descriptors) + : details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), property_descriptors, method_descriptors, event_descriptors); + + set_reply(res, status_codes::OK, class_descriptor); + } } else { From 7744ecacca1f2c1237c465f7b4dfa998623d717d Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 22 Apr 2024 13:31:24 +0100 Subject: [PATCH 099/250] Add properties endpoint (cherry picked from commit 8265d1a934f7fd80043f87214fc8e182deed4e97) (cherry picked from commit b78c25f5f91d8a80c9f335a26c4f7f782028ab18) --- Development/nmos/api_utils.h | 3 ++- Development/nmos/configuration_api.cpp | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Development/nmos/api_utils.h b/Development/nmos/api_utils.h index 3ca03b28f..3c2404126 100644 --- a/Development/nmos/api_utils.h +++ b/Development/nmos/api_utils.h @@ -91,7 +91,8 @@ namespace nmos // Configuration API const route_pattern rolePath = make_route_pattern(U("rolePath"), U("root|root\\.[a-zA-Z0-9\\-_\\.]+")); - + const route_pattern propertyId = make_route_pattern(U("propertyId"), U("^[0-9]+p[0-9]+")); + // Common patterns const route_pattern resourceId = make_route_pattern(U("resourceId"), U("[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}")); } diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 1b92e864f..95a19caf0 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -410,6 +410,17 @@ namespace nmos return pplx::task_from_result(true); }); + + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) + { + const string_t property_id = parameters.at(nmos::patterns::propertyId.name); + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); + + return pplx::task_from_result(true); + }); + return configuration_api; } + + } From 5adef6ac7a670a8f4fc58c51652629c22a50b700 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 24 Apr 2024 17:35:42 +0100 Subject: [PATCH 100/250] Update propertyId regular expression (cherry picked from commit 90598171bc0cba3d819a36b7030cea4a2358c6d0) --- Development/nmos/api_utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/api_utils.h b/Development/nmos/api_utils.h index 3c2404126..4345d5df7 100644 --- a/Development/nmos/api_utils.h +++ b/Development/nmos/api_utils.h @@ -91,7 +91,7 @@ namespace nmos // Configuration API const route_pattern rolePath = make_route_pattern(U("rolePath"), U("root|root\\.[a-zA-Z0-9\\-_\\.]+")); - const route_pattern propertyId = make_route_pattern(U("propertyId"), U("^[0-9]+p[0-9]+")); + const route_pattern propertyId = make_route_pattern(U("propertyId"), U("[0-9]+p[0-9]+")); // Common patterns const route_pattern resourceId = make_route_pattern(U("resourceId"), U("[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}")); From a4f6591dd4a9b58139b6c0e350bb4c08cc6d5083 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 26 Apr 2024 12:01:50 +0100 Subject: [PATCH 101/250] added /rolePaths/{rolePath}/property/{propertyId}/value and /rolePaths/{rolePath}/property/{propertyId}/descriptor enpoints rolePath/{rolePath/properties/{proportyId}/value endpoint --- Development/nmos/configuration_api.cpp | 111 +++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 5 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 95a19caf0..8c00368fd 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -4,8 +4,10 @@ #include //#include "cpprest/json_validator.h" #include "nmos/api_utils.h" +#include "nmos/control_protocol_methods.h" #include "nmos/control_protocol_resource.h" #include "nmos/control_protocol_state.h" +#include "nmos/control_protocol_typedefs.h" #include "nmos/control_protocol_utils.h" #include "nmos/is14_versions.h" //#include "nmos/json_schema.h" @@ -199,6 +201,15 @@ namespace nmos return get_nc_object(resources, role_path_segments); } + + nc_property_id parse_formatted_property_id(const utility::string_t& property_id) + { + const utility::string_t::size_type delimiter = property_id.find('p'); + utility::string_t level = std::string::npos != delimiter ? property_id.substr(0, delimiter) : L"0"; + utility::string_t index = std::string::npos != delimiter ? property_id.substr(delimiter + 1) : L"0"; + // JRT Hmmmm, what to do if the property_id is not of a form we recognise + return { uint16_t(web::json::value::parse(level).as_integer()), uint16_t(web::json::value::parse(index).as_integer()) }; + } } inline web::http::experimental::listener::api_router make_unmounted_configuration_api(nmos::node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate_) @@ -244,7 +255,7 @@ namespace nmos configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/?"), methods::GET, [&model, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) { - const string_t role_path = parameters.at(nmos::patterns::rolePath.name); + const utility::string_t role_path = parameters.at(nmos::patterns::rolePath.name); auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; @@ -309,7 +320,7 @@ namespace nmos configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/methods/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) { - const string_t role_path = parameters.at(nmos::patterns::rolePath.name); + const utility::string_t role_path = parameters.at(nmos::patterns::rolePath.name); auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; @@ -410,11 +421,101 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) { - const string_t property_id = parameters.at(nmos::patterns::propertyId.name); - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); + const utility::string_t property_id = parameters.at(nmos::patterns::propertyId.name); + const utility::string_t role_path = parameters.at(nmos::patterns::rolePath.name); + + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + + const auto& nc_object = details::get_nc_object(resources, role_path); + if (!nc_object.is_null()) + { + //// find the relevant nc_property_descriptor + const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)), get_control_protocol_class_descriptor); + if (property_descriptor.is_null()) + { + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); + } + else + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("descriptor/"), U("value/") }, req, res)); + } + } + else + { + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return pplx::task_from_result(true); + }); + + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/descriptor/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) + { + const utility::string_t property_id = parameters.at(nmos::patterns::propertyId.name); + const utility::string_t role_path = parameters.at(nmos::patterns::rolePath.name); + + web::json::value property_value{}; + + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + + const auto& nc_object = details::get_nc_object(resources, role_path); + if (!nc_object.is_null()) + { + //// find the relevant nc_property_descriptor + const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)), get_control_protocol_class_descriptor); + if (property_descriptor.is_null()) + { + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); + } + else + { + set_reply(res, status_codes::OK, property_descriptor); + } + } + else + { + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return pplx::task_from_result(true); + }); + + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/value/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) + { + const utility::string_t property_id = parameters.at(nmos::patterns::propertyId.name); + const utility::string_t role_path = parameters.at(nmos::patterns::rolePath.name); + + web::json::value property_value{}; + + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + + const auto& nc_object = details::get_nc_object(resources, role_path); + if (!nc_object.is_null()) + { + //// find the relevant nc_property_descriptor + const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)), get_control_protocol_class_descriptor); + if (property_descriptor.is_null()) + { + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); + } + else + { + web::json::value response = web::json::value_of({ + { nmos::fields::nc::status, nmos::fields::nc::is_deprecated(property_descriptor) ? nmos::nc_method_status::property_deprecated : nmos::nc_method_status::ok }, + { nmos::fields::nc::value, nc_object.at(nmos::fields::nc::name(property_descriptor)) } + }); + + set_reply(res, status_codes::OK, response); + } + } + else + { + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } return pplx::task_from_result(true); }); From 6788b1f9432087e7022da54ff4e22c61e7a41691 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 26 Apr 2024 12:33:56 +0100 Subject: [PATCH 102/250] Fixed property id parsing --- Development/nmos/configuration_api.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 8c00368fd..9798e7840 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -204,10 +204,10 @@ namespace nmos nc_property_id parse_formatted_property_id(const utility::string_t& property_id) { + // Assume that property_id is in form "p" as validated by the propertyId regular expression pattern const utility::string_t::size_type delimiter = property_id.find('p'); - utility::string_t level = std::string::npos != delimiter ? property_id.substr(0, delimiter) : L"0"; - utility::string_t index = std::string::npos != delimiter ? property_id.substr(delimiter + 1) : L"0"; - // JRT Hmmmm, what to do if the property_id is not of a form we recognise + utility::string_t level = property_id.substr(0, delimiter); + utility::string_t index = property_id.substr(delimiter + 1); return { uint16_t(web::json::value::parse(level).as_integer()), uint16_t(web::json::value::parse(index).as_integer()) }; } } From e9306f2f79cdacbb4139a955090eb470c89e340f Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 26 Apr 2024 14:57:42 +0100 Subject: [PATCH 103/250] Expose make_nc_method_result utility function --- Development/nmos/control_protocol_resource.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index a03542264..c43d82977 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -40,6 +40,9 @@ namespace nmos namespace details { + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethodresult + web::json::value make_nc_method_result(const nc_method_result& method_result, const web::json::value& value); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncelementid web::json::value make_nc_element_id(const nc_element_id& element_id); nc_element_id parse_nc_element_id(const web::json::value& element_id); From f0016404baaacb5ff344ceb65abc838634cfa60d Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 26 Apr 2024 14:58:05 +0100 Subject: [PATCH 104/250] Refactor method result creation --- Development/nmos/configuration_api.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 9798e7840..f1f1356ca 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -504,12 +504,9 @@ namespace nmos } else { - web::json::value response = web::json::value_of({ - { nmos::fields::nc::status, nmos::fields::nc::is_deprecated(property_descriptor) ? nmos::nc_method_status::property_deprecated : nmos::nc_method_status::ok }, - { nmos::fields::nc::value, nc_object.at(nmos::fields::nc::name(property_descriptor)) } - }); + web::json::value method_result = details::make_nc_method_result({ nmos::fields::nc::is_deprecated(property_descriptor) ? nmos::nc_method_status::property_deprecated : nmos::nc_method_status::ok }, nc_object.at(nmos::fields::nc::name(property_descriptor))); - set_reply(res, status_codes::OK, response); + set_reply(res, status_codes::OK, method_result); } } else From c1d3c10e93d9c4079ce359a34124cfda6700a595 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 26 Apr 2024 16:30:08 +0100 Subject: [PATCH 105/250] Fix descriptor endpoints to return NcMethodResult objects instead of raw descriptors --- Development/nmos/configuration_api.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index f1f1356ca..870eab422 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -410,7 +410,8 @@ namespace nmos ? details::make_nc_class_descriptor(description, class_id, name, property_descriptors, method_descriptors, event_descriptors) : details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), property_descriptors, method_descriptors, event_descriptors); - set_reply(res, status_codes::OK, class_descriptor); + web::json::value method_result = details::make_nc_method_result({ nmos::nc_method_status::ok }, class_descriptor); + set_reply(res, status_codes::OK, method_result); } } else @@ -472,7 +473,8 @@ namespace nmos } else { - set_reply(res, status_codes::OK, property_descriptor); + web::json::value method_result = details::make_nc_method_result({ nmos::nc_method_status::ok }, property_descriptor); + set_reply(res, status_codes::OK, method_result); } } else From 014cb37c6767722dfcd7dc9eb05e2d7a055e61d5 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Mon, 29 Apr 2024 08:18:09 +0100 Subject: [PATCH 106/250] Tidy up --- Development/nmos/configuration_api.cpp | 56 +++++++++++--------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 870eab422..13d9b6c6f 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -228,10 +228,8 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.support(U("/rolePaths/?"), methods::GET, [&model](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/?"), methods::GET, [&model](http_request req, http_response res, const string_t&, const route_parameters&) { - using web::json::value; - auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; @@ -253,9 +251,9 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/?"), methods::GET, [&model, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/?"), methods::GET, [&model, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { - const utility::string_t role_path = parameters.at(nmos::patterns::rolePath.name); + const auto role_path = parameters.at(nmos::patterns::rolePath.name); auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; @@ -272,9 +270,9 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { - const string_t role_path = parameters.at(nmos::patterns::rolePath.name); + const auto role_path = parameters.at(nmos::patterns::rolePath.name); auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; @@ -318,9 +316,9 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/methods/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/methods/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { - const utility::string_t role_path = parameters.at(nmos::patterns::rolePath.name); + const auto role_path = parameters.at(nmos::patterns::rolePath.name); auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; @@ -330,7 +328,7 @@ namespace nmos { std::set methods_routes; - nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)); + auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)); while (!class_id.empty()) { const auto& control_class = get_control_protocol_class_descriptor(class_id); @@ -366,11 +364,9 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/descriptor/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/descriptor/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { - using web::json::value_from_elements; - - const string_t role_path = parameters.at(nmos::patterns::rolePath.name); + const auto role_path = parameters.at(nmos::patterns::rolePath.name); auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; @@ -410,7 +406,7 @@ namespace nmos ? details::make_nc_class_descriptor(description, class_id, name, property_descriptors, method_descriptors, event_descriptors) : details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), property_descriptors, method_descriptors, event_descriptors); - web::json::value method_result = details::make_nc_method_result({ nmos::nc_method_status::ok }, class_descriptor); + auto method_result = details::make_nc_method_result({ nmos::nc_method_status::ok }, class_descriptor); set_reply(res, status_codes::OK, method_result); } } @@ -422,10 +418,10 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { - const utility::string_t property_id = parameters.at(nmos::patterns::propertyId.name); - const utility::string_t role_path = parameters.at(nmos::patterns::rolePath.name); + const auto property_id = parameters.at(nmos::patterns::propertyId.name); + const auto role_path = parameters.at(nmos::patterns::rolePath.name); auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; @@ -433,7 +429,7 @@ namespace nmos const auto& nc_object = details::get_nc_object(resources, role_path); if (!nc_object.is_null()) { - //// find the relevant nc_property_descriptor + // find the relevant nc_property_descriptor const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { @@ -452,12 +448,10 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/descriptor/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/descriptor/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { - const utility::string_t property_id = parameters.at(nmos::patterns::propertyId.name); - const utility::string_t role_path = parameters.at(nmos::patterns::rolePath.name); - - web::json::value property_value{}; + const auto property_id = parameters.at(nmos::patterns::propertyId.name); + const auto role_path = parameters.at(nmos::patterns::rolePath.name); auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; @@ -465,7 +459,7 @@ namespace nmos const auto& nc_object = details::get_nc_object(resources, role_path); if (!nc_object.is_null()) { - //// find the relevant nc_property_descriptor + // find the relevant nc_property_descriptor const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { @@ -485,12 +479,10 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/value/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t& route_path, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/value/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { - const utility::string_t property_id = parameters.at(nmos::patterns::propertyId.name); - const utility::string_t role_path = parameters.at(nmos::patterns::rolePath.name); - - web::json::value property_value{}; + const auto property_id = parameters.at(nmos::patterns::propertyId.name); + const auto role_path = parameters.at(nmos::patterns::rolePath.name); auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; @@ -498,7 +490,7 @@ namespace nmos const auto& nc_object = details::get_nc_object(resources, role_path); if (!nc_object.is_null()) { - //// find the relevant nc_property_descriptor + // find the relevant nc_property_descriptor const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { @@ -521,6 +513,4 @@ namespace nmos return configuration_api; } - - } From 514468f4fae9c4797379163d543a0de9e6f6bd4b Mon Sep 17 00:00:00 2001 From: lo-simon Date: Mon, 29 Apr 2024 09:04:35 +0100 Subject: [PATCH 107/250] Add common functions and rename get_nc_object to get_nc_resource --- Development/nmos/configuration_api.cpp | 92 +++++++++++++++----------- 1 file changed, 54 insertions(+), 38 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 13d9b6c6f..56bea7ef5 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -161,7 +161,7 @@ namespace nmos return web::json::value{}; } - web::json::value get_nc_object(const resources& resources, std::list& role_path_segments) + web::json::value get_nc_resource(const resources& resources, std::list& role_path_segments) { auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); if (resources.end() != resource) @@ -193,23 +193,50 @@ namespace nmos return web::json::value{}; } - web::json::value get_nc_object(const resources& resources, const utility::string_t& role_path) + web::json::value get_nc_resource(const resources& resources, const utility::string_t& role_path) { // tokenize the role_path with the '.' delimiter std::list role_path_segments; boost::algorithm::split(role_path_segments, role_path, [](utility::char_t c) { return '.' == c; }); - return get_nc_object(resources, role_path_segments); + return get_nc_resource(resources, role_path_segments); } nc_property_id parse_formatted_property_id(const utility::string_t& property_id) { // Assume that property_id is in form "p" as validated by the propertyId regular expression pattern const utility::string_t::size_type delimiter = property_id.find('p'); - utility::string_t level = property_id.substr(0, delimiter); - utility::string_t index = property_id.substr(delimiter + 1); + auto level = property_id.substr(0, delimiter); + auto index = property_id.substr(delimiter + 1); return { uint16_t(web::json::value::parse(level).as_integer()), uint16_t(web::json::value::parse(index).as_integer()) }; } + + // format nc_property_id to the form of "p" + utility::string_t make_formatted_property_id(const web::json::value& property_descriptor) + { + auto property_id = nmos::fields::nc::id(property_descriptor); + utility::ostringstream_t os; + os << nmos::fields::nc::level(property_id) << 'p' << nmos::fields::nc::index(property_id); + return os.str(); + } + + nc_method_id parse_formatted_method_id(const utility::string_t& method_id) + { + // Assume that method_id is in form "m" as validated by the methodId regular expression pattern + const utility::string_t::size_type delimiter = method_id.find('m'); + auto level = method_id.substr(0, delimiter); + auto index = method_id.substr(delimiter + 1); + return { uint16_t(web::json::value::parse(level).as_integer()), uint16_t(web::json::value::parse(index).as_integer()) }; + } + + // format nc_method_id to the form of "m" + utility::string_t make_formatted_method_id(const web::json::value& method_descriptor) + { + auto method_id = nmos::fields::nc::id(method_descriptor); + utility::ostringstream_t os; + os << nmos::fields::nc::level(method_id) << 'm' << nmos::fields::nc::index(method_id); + return os.str(); + } } inline web::http::experimental::listener::api_router make_unmounted_configuration_api(nmos::node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate_) @@ -258,7 +285,7 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - if (!details::get_nc_object(resources, role_path).is_null()) + if (!details::get_nc_resource(resources, role_path).is_null()) { set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("bulkProperties/"), U("descriptor/"), U("methods/"), U("properties/") }, req, res)); } @@ -277,12 +304,12 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& nc_object = details::get_nc_object(resources, role_path); - if (!nc_object.is_null()) + const auto& resource = details::get_nc_resource(resources, role_path); + if (!resource.is_null()) { std::set properties_routes; - auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)); + auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource)); while (!class_id.empty()) { const auto& control_class = get_control_protocol_class_descriptor(class_id); @@ -290,15 +317,7 @@ namespace nmos auto properties_route = boost::copy_range>(property_descriptors | boost::adaptors::transformed([](const web::json::value& property_descriptor) { - auto make_property_id = [](const web::json::value& property_descriptor) - { - auto property_id = nmos::fields::nc::id(property_descriptor); - utility::ostringstream_t os; - os << nmos::fields::nc::level(property_id) << 'p' << nmos::fields::nc::index(property_id); - return os.str(); - }; - - return make_property_id(property_descriptor) + U("/"); + return details::make_formatted_property_id(property_descriptor) + U("/"); })); properties_routes.insert(properties_route.begin(), properties_route.end()); @@ -323,12 +342,12 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& nc_object = details::get_nc_object(resources, role_path); - if (!nc_object.is_null()) + const auto& resource = details::get_nc_resource(resources, role_path); + if (!resource.is_null()) { std::set methods_routes; - auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)); + auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource)); while (!class_id.empty()) { const auto& control_class = get_control_protocol_class_descriptor(class_id); @@ -340,10 +359,7 @@ namespace nmos { // method tuple definition described in control_protocol_handlers.h auto& nc_method_descriptor = std::get<0>(method); - auto method_id = nmos::fields::nc::id(nc_method_descriptor); - utility::ostringstream_t os; - os << nmos::fields::nc::level(method_id) << 'm' << nmos::fields::nc::index(method_id); - return os.str(); + return details::make_formatted_method_id(nc_method_descriptor); }; return make_method_id(method) + U("/"); @@ -371,10 +387,10 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& nc_object = details::get_nc_object(resources, role_path); - if (!nc_object.is_null()) + const auto& resource = details::get_nc_resource(resources, role_path); + if (!resource.is_null()) { - nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)); + nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource)); if (!class_id.empty()) { @@ -426,11 +442,11 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& nc_object = details::get_nc_object(resources, role_path); - if (!nc_object.is_null()) + const auto& resource = details::get_nc_resource(resources, role_path); + if (!resource.is_null()) { // find the relevant nc_property_descriptor - const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)), get_control_protocol_class_descriptor); + const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); @@ -456,11 +472,11 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& nc_object = details::get_nc_object(resources, role_path); - if (!nc_object.is_null()) + const auto& resource = details::get_nc_resource(resources, role_path); + if (!resource.is_null()) { // find the relevant nc_property_descriptor - const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)), get_control_protocol_class_descriptor); + const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); @@ -487,18 +503,18 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& nc_object = details::get_nc_object(resources, role_path); - if (!nc_object.is_null()) + const auto& resource = details::get_nc_resource(resources, role_path); + if (!resource.is_null()) { // find the relevant nc_property_descriptor - const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(nc_object)), get_control_protocol_class_descriptor); + const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); } else { - web::json::value method_result = details::make_nc_method_result({ nmos::fields::nc::is_deprecated(property_descriptor) ? nmos::nc_method_status::property_deprecated : nmos::nc_method_status::ok }, nc_object.at(nmos::fields::nc::name(property_descriptor))); + web::json::value method_result = details::make_nc_method_result({ nmos::fields::nc::is_deprecated(property_descriptor) ? nmos::nc_method_status::property_deprecated : nmos::nc_method_status::ok }, resource.at(nmos::fields::nc::name(property_descriptor))); set_reply(res, status_codes::OK, method_result); } From b2ae537d9212a9608ffa2ece29fa1ce43efa61e8 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 8 May 2024 16:09:34 +0100 Subject: [PATCH 108/250] Add /rolePaths/{rolePath}/methods/{methodId} PATCH and /rolePaths/{rolePath}/properties/{propertyId}/value PUT endpoints --- Development/nmos/api_utils.h | 3 +- Development/nmos/configuration_api.cpp | 264 ++++++++++++++----- Development/nmos/configuration_api.h | 2 +- Development/nmos/control_protocol_resource.h | 2 + Development/nmos/json_schema.cpp | 57 ++++ Development/nmos/json_schema.h | 5 + Development/nmos/node_server.cpp | 2 +- 7 files changed, 261 insertions(+), 74 deletions(-) diff --git a/Development/nmos/api_utils.h b/Development/nmos/api_utils.h index 4345d5df7..f6ef29e62 100644 --- a/Development/nmos/api_utils.h +++ b/Development/nmos/api_utils.h @@ -92,7 +92,8 @@ namespace nmos // Configuration API const route_pattern rolePath = make_route_pattern(U("rolePath"), U("root|root\\.[a-zA-Z0-9\\-_\\.]+")); const route_pattern propertyId = make_route_pattern(U("propertyId"), U("[0-9]+p[0-9]+")); - + const route_pattern methodId = make_route_pattern(U("methodId"), U("[0-9]+m[0-9]+")); + // Common patterns const route_pattern resourceId = make_route_pattern(U("resourceId"), U("[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}")); } diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 56bea7ef5..6b1807c7a 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -2,23 +2,25 @@ #include #include -//#include "cpprest/json_validator.h" +#include +#include "cpprest/json_validator.h" #include "nmos/api_utils.h" +#include "nmos/control_protocol_handlers.h" #include "nmos/control_protocol_methods.h" #include "nmos/control_protocol_resource.h" #include "nmos/control_protocol_state.h" #include "nmos/control_protocol_typedefs.h" #include "nmos/control_protocol_utils.h" #include "nmos/is14_versions.h" -//#include "nmos/json_schema.h" +#include "nmos/json_schema.h" #include "nmos/log_manip.h" #include "nmos/model.h" namespace nmos { - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(nmos::node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -49,7 +51,7 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, gate)); + configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, property_changed, gate)); return configuration_api; } @@ -63,40 +65,6 @@ namespace nmos const web::json::field_as_string_or describe{ U("describe"), {} }; } - utility::string_t make_query_parameters(web::json::value flat_query_params) - { - // any non-string query parameters need serializing before encoding - - // all other string values need encoding - nmos::details::encode_elements(flat_query_params); - - return web::json::query_from_value(flat_query_params); - } - - web::json::value parse_query_parameters(const utility::string_t& query) - { - auto flat_query_params = web::json::value_from_query(query); - - // all other string values need decoding - nmos::details::decode_elements(flat_query_params); - - // any non-string query parameters need parsing after decoding... - if (flat_query_params.has_field(nmos::fields::nc::level)) - { - flat_query_params[nmos::fields::nc::level] = web::json::value::parse(nmos::details::fields::level(flat_query_params)); - } - if (flat_query_params.has_field(nmos::details::fields::index)) - { - flat_query_params[nmos::details::fields::index] = web::json::value::parse(nmos::details::fields::index(flat_query_params)); - } - if (flat_query_params.has_field(nmos::details::fields::describe)) - { - flat_query_params[nmos::details::fields::describe] = web::json::value::parse(nmos::details::fields::describe(flat_query_params)); - } - - return flat_query_params; - } - void build_role_paths(const resources& resources, const nmos::resource& resource, const utility::string_t& base_role_path, std::set& role_paths) { if (resource.data.has_field(nmos::fields::nc::members)) @@ -113,7 +81,7 @@ namespace nmos { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(member); - const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != found) { build_role_paths(resources, *found, role_path, role_paths); @@ -150,7 +118,7 @@ namespace nmos { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(*member_found); - const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != found) { return get_nc_block_member_descriptor(resources, *found, role_path_segments); @@ -161,7 +129,7 @@ namespace nmos return web::json::value{}; } - web::json::value get_nc_resource(const resources& resources, std::list& role_path_segments) + resources::const_iterator find_resource(const resources& resources, std::list& role_path_segments) { auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); if (resources.end() != resource) @@ -177,29 +145,29 @@ namespace nmos if (!block_member_descriptor.is_null()) { const auto& oid = nmos::fields::nc::oid(block_member_descriptor); - const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != found) { - return found->data; + return found; } } } else { - return resource->data; + return resource; } } } - return web::json::value{}; + return resources.end(); } - web::json::value get_nc_resource(const resources& resources, const utility::string_t& role_path) + resources::const_iterator find_resource(const resources& resources, const utility::string_t& role_path) { // tokenize the role_path with the '.' delimiter std::list role_path_segments; boost::algorithm::split(role_path_segments, role_path, [](utility::char_t c) { return '.' == c; }); - return get_nc_resource(resources, role_path_segments); + return find_resource(resources, role_path_segments); } nc_property_id parse_formatted_property_id(const utility::string_t& property_id) @@ -237,9 +205,23 @@ namespace nmos os << nmos::fields::nc::level(method_id) << 'm' << nmos::fields::nc::index(method_id); return os.str(); } + + static const web::json::experimental::json_validator& configurationapi_validator() + { + // hmm, could be based on supported API versions from settings, like other APIs' validators? + static const web::json::experimental::json_validator validator + { + nmos::experimental::load_json_schema, + boost::copy_range>(boost::range::join( + is14_versions::all | boost::adaptors::transformed(experimental::make_configrationapi_method_patch_request_schema_uri), + is14_versions::all | boost::adaptors::transformed(experimental::make_configrationapi_property_value_put_request_schema_uri) + )) + }; + return validator; + } } - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(nmos::node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate_) + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -284,13 +266,15 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; + const auto& resource = details::find_resource(resources, role_path); - if (!details::get_nc_resource(resources, role_path).is_null()) + if (resources.end() != resource) { set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("bulkProperties/"), U("descriptor/"), U("methods/"), U("properties/") }, req, res)); } else { + // resource not found for the role path set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); } @@ -304,12 +288,12 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = details::get_nc_resource(resources, role_path); - if (!resource.is_null()) + const auto& resource = details::find_resource(resources, role_path); + if (resources.end() != resource) { std::set properties_routes; - auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource)); + auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); while (!class_id.empty()) { const auto& control_class = get_control_protocol_class_descriptor(class_id); @@ -329,6 +313,7 @@ namespace nmos } else { + // resource not found for the role path set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); } @@ -342,12 +327,12 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = details::get_nc_resource(resources, role_path); - if (!resource.is_null()) + const auto& resource = details::find_resource(resources, role_path); + if (resources.end() != resource) { std::set methods_routes; - auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource)); + auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); while (!class_id.empty()) { const auto& control_class = get_control_protocol_class_descriptor(class_id); @@ -374,6 +359,7 @@ namespace nmos } else { + // resource not found for the role path set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); } @@ -387,10 +373,10 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = details::get_nc_resource(resources, role_path); - if (!resource.is_null()) + const auto& resource = details::find_resource(resources, role_path); + if (resources.end() != resource) { - nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource)); + nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); if (!class_id.empty()) { @@ -428,6 +414,7 @@ namespace nmos } else { + // resource not found for the role path set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); } @@ -442,11 +429,11 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = details::get_nc_resource(resources, role_path); - if (!resource.is_null()) + const auto& resource = details::find_resource(resources, role_path); + if (resources.end() != resource) { // find the relevant nc_property_descriptor - const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource)), get_control_protocol_class_descriptor); + const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); @@ -458,6 +445,7 @@ namespace nmos } else { + // resource not found for the role path set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); } @@ -472,23 +460,24 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = details::get_nc_resource(resources, role_path); - if (!resource.is_null()) + const auto& resource = details::find_resource(resources, role_path); + if (resources.end() != resource) { // find the relevant nc_property_descriptor - const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource)), get_control_protocol_class_descriptor); + const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); } else { - web::json::value method_result = details::make_nc_method_result({ nmos::nc_method_status::ok }, property_descriptor); + auto method_result = details::make_nc_method_result({ nmos::nc_method_status::ok }, property_descriptor); set_reply(res, status_codes::OK, method_result); } } else { + // resource not found for the role path set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); } @@ -503,30 +492,163 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = details::get_nc_resource(resources, role_path); - if (!resource.is_null()) + const auto& resource = details::find_resource(resources, role_path); + if (resources.end() != resource) { // find the relevant nc_property_descriptor - const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource)), get_control_protocol_class_descriptor); + const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); } else { - web::json::value method_result = details::make_nc_method_result({ nmos::fields::nc::is_deprecated(property_descriptor) ? nmos::nc_method_status::property_deprecated : nmos::nc_method_status::ok }, resource.at(nmos::fields::nc::name(property_descriptor))); - + auto method_result = details::make_nc_method_result({ nmos::fields::nc::is_deprecated(property_descriptor) ? nmos::nc_method_status::property_deprecated : nmos::nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(property_descriptor))); set_reply(res, status_codes::OK, method_result); } } else { + // resource not found for the role path set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); } return pplx::task_from_result(true); }); + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/methods/") + nmos::patterns::methodId.pattern + U("/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, property_changed, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + nmos::api_gate gate(gate_, req, parameters); + return details::extract_json(req, gate).then([&model, req, res, parameters, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate](value body) mutable + { + auto lock = model.write_lock(); + + const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); + + // Validate JSON syntax according to the schema + details::configurationapi_validator().validate(body, experimental::make_configrationapi_method_patch_request_schema_uri(version)); + + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + const auto method_id = parameters.at(nmos::patterns::methodId.name); + + auto& resources = model.control_protocol_resources; + auto& arguments = nmos::fields::nc::arguments(body); + + const auto& resource = details::find_resource(resources, role_path); + if (resources.end() != resource) + { + auto method = get_control_protocol_method_descriptor(details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), details::parse_formatted_method_id(method_id)); + auto& nc_method_descriptor = std::get<0>(method); + auto& standard_method = std::get<1>(method); + auto& non_standard_method = std::get<2>(method); + web::http::status_code code{ status_codes::BadRequest }; + value method_result; + + if (standard_method || non_standard_method) + { + try + { + // do method arguments constraints validation + method_parameters_contraints_validation(arguments, nc_method_descriptor, get_control_protocol_datatype_descriptor); + + // execute the relevant method handler, then accumulating up their response to reponses + if (standard_method) + { + method_result = standard_method(resources, *resource, 0, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate).at(nmos::fields::nc::result); + } + else // non_standard_method + { + method_result = non_standard_method(resources, *resource, 0, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), gate).at(nmos::fields::nc::result); + } + + auto status = nmos::fields::nc::status(method_result); + if (nc_method_status::ok == status) { code = status_codes::OK; } + else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } + else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } + else { code = status_codes::InternalError; } + } + catch (const nmos::control_protocol_exception& e) + { + // invalid arguments + utility::stringstream_t ss; + ss << U("invalid argument: ") << arguments.serialize() << " error: " << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + + code = status_codes::BadRequest; + } + } + else + { + // unknown methodId + utility::stringstream_t ss; + ss << U("unsupported method_id: ") << method_id + << U(" for control class class_id: ") << resource->data.at(nmos::fields::nc::class_id).serialize(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, ss.str()); + + code = status_codes::NotFound; + } + set_reply(res, code, method_result); + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return true; + }); + }); + + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/value/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + nmos::api_gate gate(gate_, req, parameters); + return details::extract_json(req, gate).then([&model, req, res, parameters, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate](value body) mutable + { + auto lock = model.write_lock(); + + const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); + + // Validate JSON syntax according to the schema + details::configurationapi_validator().validate(body, experimental::make_configrationapi_property_value_put_request_schema_uri(version)); + + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + const auto property_id = parameters.at(nmos::patterns::propertyId.name); + + auto& resources = model.control_protocol_resources; + + const auto& resource = details::find_resource(resources, role_path); + if (resources.end() != resource) + { + // find the relevant nc_property_descriptor + const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); + if (property_descriptor.is_null()) + { + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); + } + else + { + auto arguments = value_of({ + { nmos::fields::nc::id, details::make_nc_property_id(details::parse_formatted_property_id(property_id))}, + }); + web::json::merge_patch(arguments, body, true); + + auto result = set(resources, *resource, 0, arguments, false, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate).at(nmos::fields::nc::result); + + auto status = nmos::fields::nc::status(result); + auto code = nc_method_status::ok == status ? status_codes::OK : status_codes::InternalError; + set_reply(res, code, result); + } + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return true; + }); + }); + return configuration_api; } } diff --git a/Development/nmos/configuration_api.h b/Development/nmos/configuration_api.h index 8c6795b40..9b1a73297 100644 --- a/Development/nmos/configuration_api.h +++ b/Development/nmos/configuration_api.h @@ -15,7 +15,7 @@ namespace nmos { struct node_model; - web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); } #endif diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index c43d82977..57fa13063 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -41,6 +41,8 @@ namespace nmos namespace details { // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethodresult + web::json::value make_nc_method_result(const nc_method_result& method_result); + web::json::value make_nc_method_result_error(const nc_method_result& method_result, const utility::string_t& error_message); web::json::value make_nc_method_result(const nc_method_result& method_result, const web::json::value& value); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncelementid diff --git a/Development/nmos/json_schema.cpp b/Development/nmos/json_schema.cpp index d24f2a5df..99220aab5 100644 --- a/Development/nmos/json_schema.cpp +++ b/Development/nmos/json_schema.cpp @@ -12,6 +12,8 @@ #include "nmos/is10_schemas/is10_schemas.h" #include "nmos/is12_versions.h" #include "nmos/is12_schemas/is12_schemas.h" +#include "nmos/is14_versions.h" +#include "nmos/is14_schemas/is14_schemas.h" #include "nmos/type.h" namespace nmos @@ -170,6 +172,26 @@ namespace nmos const web::uri controlprotocolapi_subscription_message_schema_uri = make_schema_uri(tag, _XPLATSTR("subscription-message.json")); } } + + namespace is14_schemas + { + web::uri make_schema_uri(const utility::string_t& tag, const utility::string_t& ref = {}) + { + return{ _XPLATSTR("https://github.com/AMWA-TV/is-14/raw/") + tag + _XPLATSTR("/APIs/schemas/") + ref }; + } + + // See https://github.com/AMWA-TV/is-14/tree/v1.0-dev/APIs/schemas/ + namespace v1_0 + { + using namespace nmos::is14_schemas::v1_0_x; + const utility::string_t tag(_XPLATSTR("v1.0.x")); + + const web::uri configrationapi_bulkProperties_set_request_schema_uri = make_schema_uri(tag, _XPLATSTR("bulkProperties-set-request.json")); + const web::uri configrationapi_bulkProperties_validate_request_schema_uri = make_schema_uri(tag, _XPLATSTR("bulkProperties-validate-request.json")); + const web::uri configrationapi_method_patch_request_schema_uri = make_schema_uri(tag, _XPLATSTR("method-patch-request.json")); + const web::uri configrationapi_property_value_put_request_schema_uri = make_schema_uri(tag, _XPLATSTR("property-value-put-request.json")); + } + } } namespace nmos @@ -391,6 +413,20 @@ namespace nmos }; } + static std::map make_is14_schemas() + { + using namespace nmos::is14_schemas; + + return + { + // v1.0 + { make_schema_uri(v1_0::tag, _XPLATSTR("bulkProperties-set-request.json")), make_schema(v1_0::bulkProperties_set_request) }, + { make_schema_uri(v1_0::tag, _XPLATSTR("bulkProperties-validate-request.json")), make_schema(v1_0::bulkProperties_validate_request) }, + { make_schema_uri(v1_0::tag, _XPLATSTR("method-patch-request.json")), make_schema(v1_0::method_patch_request) }, + { make_schema_uri(v1_0::tag, _XPLATSTR("property-value-put-request.json")), make_schema(v1_0::property_value_put_request) } + }; + } + inline void merge(std::map& to, std::map&& from) { to.insert(from.begin(), from.end()); // std::map::merge in C++17 @@ -404,6 +440,7 @@ namespace nmos merge(result, make_is09_schemas()); merge(result, make_is10_schemas()); merge(result, make_is12_schemas()); + merge(result, make_is14_schemas()); return result; } @@ -510,6 +547,26 @@ namespace nmos return is12_schemas::v1_0::controlprotocolapi_subscription_message_schema_uri; } + web::uri make_configrationapi_bulkProperties_set_request_schema_uri(const nmos::api_version& version) + { + return is14_schemas::v1_0::configrationapi_bulkProperties_set_request_schema_uri; + } + + web::uri make_configrationapi_bulkProperties_validate_request_schema_uri(const nmos::api_version& version) + { + return is14_schemas::v1_0::configrationapi_bulkProperties_validate_request_schema_uri; + } + + web::uri make_configrationapi_method_patch_request_schema_uri(const nmos::api_version& version) + { + return is14_schemas::v1_0::configrationapi_method_patch_request_schema_uri; + } + + web::uri make_configrationapi_property_value_put_request_schema_uri(const nmos::api_version& version) + { + return is14_schemas::v1_0::configrationapi_property_value_put_request_schema_uri; + } + // load the json schema for the specified base URI web::json::value load_json_schema(const web::uri& id) { diff --git a/Development/nmos/json_schema.h b/Development/nmos/json_schema.h index 57cb0996b..6c98d8a66 100644 --- a/Development/nmos/json_schema.h +++ b/Development/nmos/json_schema.h @@ -40,6 +40,11 @@ namespace nmos web::uri make_controlprotocolapi_command_message_schema_uri(const nmos::api_version& version); web::uri make_controlprotocolapi_subscription_message_schema_uri(const nmos::api_version& version); + web::uri make_configrationapi_bulkProperties_set_request_schema_uri(const nmos::api_version& version); + web::uri make_configrationapi_bulkProperties_validate_request_schema_uri(const nmos::api_version& version); + web::uri make_configrationapi_method_patch_request_schema_uri(const nmos::api_version& version); + web::uri make_configrationapi_property_value_put_request_schema_uri(const nmos::api_version& version); + // load the json schema for the specified base URI web::json::value load_json_schema(const web::uri& id); } diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index 710b31d95..1e9a97da2 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -77,7 +77,7 @@ namespace nmos // Configure the Configuration API - node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, gate)); + node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.control_protocol_property_changed, gate)); const auto& events_ws_port = nmos::fields::events_ws_port(node_model.settings); auto& events_ws_api = node_server.ws_handlers[{ {}, nmos::fields::events_ws_port(node_model.settings) }]; From 1e28e1601517dc0764ed19d524f483b5b0db4fdd Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 10 May 2024 15:51:47 +0100 Subject: [PATCH 109/250] Make deprecated properties and methods return OK --- Development/nmos/configuration_api.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 6b1807c7a..f59b48976 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -562,7 +562,7 @@ namespace nmos } auto status = nmos::fields::nc::status(method_result); - if (nc_method_status::ok == status) { code = status_codes::OK; } + if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } else { code = status_codes::InternalError; } @@ -635,7 +635,7 @@ namespace nmos auto result = set(resources, *resource, 0, arguments, false, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate).at(nmos::fields::nc::result); auto status = nmos::fields::nc::status(result); - auto code = nc_method_status::ok == status ? status_codes::OK : status_codes::InternalError; + auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; set_reply(res, code, result); } } From b54931a8074e73d9bd7d68ecaf15c518d677b644 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 17 May 2024 14:52:17 +0100 Subject: [PATCH 110/250] Pull command handle up to IS-12 command handling level to standard methods more generic --- .../nmos-cpp-node/node_implementation.cpp | 12 +- Development/nmos/configuration_api.cpp | 6 +- Development/nmos/control_protocol_handlers.h | 4 +- Development/nmos/control_protocol_methods.cpp | 118 +++++++++--------- Development/nmos/control_protocol_methods.h | 26 ++-- .../nmos/control_protocol_resource.cpp | 9 ++ Development/nmos/control_protocol_resource.h | 1 + Development/nmos/control_protocol_ws_api.cpp | 15 ++- 8 files changed, 102 insertions(+), 89 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index bba973805..f12cca15e 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1008,31 +1008,31 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr nmos::experimental::make_control_class_property_descriptor(U("Example object sequence property"), { 3, 14 }, object_sequence, U("ExampleDataType"), false, false, true) }; - auto example_method_with_no_args = [](nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + auto example_method_with_no_args = [](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... slog::log(gate, SLOG_FLF) << "Executing the example method with no arguments"; - return nmos::make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); + return nmos::details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); }; - auto example_method_with_simple_args = [](nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + auto example_method_with_simple_args = [](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... // and the method parameters constriants has already been validated by the outer function slog::log(gate, SLOG_FLF) << "Executing the example method with simple arguments: " << arguments.serialize(); - return nmos::make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); + return nmos::details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); }; - auto example_method_with_object_args = [](nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + auto example_method_with_object_args = [](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... // and the method parameters constriants has already been validated by the outer function slog::log(gate, SLOG_FLF) << "Executing the example method with object argument: " << arguments.serialize(); - return nmos::make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); + return nmos::details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); }; // Example control class method descriptors std::vector example_control_method_descriptors = diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index f59b48976..8d9481f0a 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -554,11 +554,11 @@ namespace nmos // execute the relevant method handler, then accumulating up their response to reponses if (standard_method) { - method_result = standard_method(resources, *resource, 0, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate).at(nmos::fields::nc::result); + method_result = standard_method(resources, *resource, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate); } else // non_standard_method { - method_result = non_standard_method(resources, *resource, 0, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), gate).at(nmos::fields::nc::result); + method_result = non_standard_method(resources, *resource, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), gate); } auto status = nmos::fields::nc::status(method_result); @@ -632,7 +632,7 @@ namespace nmos }); web::json::merge_patch(arguments, body, true); - auto result = set(resources, *resource, 0, arguments, false, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate).at(nmos::fields::nc::result); + auto result = set(resources, *resource, arguments, false, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate); auto status = nmos::fields::nc::status(result); auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index 89c6dd032..cbc62907e 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -39,10 +39,10 @@ namespace nmos namespace experimental { // standard method handler definition - typedef std::function standard_method_handler; + typedef std::function standard_method_handler; // non-standard method handler definition - typedef std::function non_standard_method_handler; + typedef std::function non_standard_method_handler; // method definition (NcMethodDescriptor vs method handler) typedef std::tuple method; diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index 8bdd673c8..7106b0e25 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -12,7 +12,7 @@ namespace nmos { // NcObject methods implementation // Get property value - web::json::value get(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + web::json::value get(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -24,17 +24,17 @@ namespace nmos const auto& property = find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, resource.data.at(nmos::fields::nc::name(property))); + return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, resource.data.at(nmos::fields::nc::name(property))); } // unknown property utility::stringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do Get"); - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + return details::make_nc_method_result_error({ nc_method_status::property_not_implemented }, ss.str()); } // Set property value - web::json::value set(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + web::json::value set(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -50,14 +50,14 @@ namespace nmos { if (nmos::fields::nc::is_read_only(property)) { - return make_control_protocol_message_response(handle, { nc_method_status::read_only }); + return details::make_nc_method_result({ nc_method_status::read_only }); } if ((val.is_null() && !nmos::fields::nc::is_nullable(property)) || (!val.is_array() && nmos::fields::nc::is_sequence(property)) || (val.is_array() && !nmos::fields::nc::is_sequence(property))) { - return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); + return details::make_nc_method_result({ nc_method_status::parameter_error }); } try @@ -78,24 +78,24 @@ namespace nmos }, make_property_changed_event(nmos::fields::nc::oid(resource.data), { { property_id_, nc_property_change_type::type::value_changed, val } })); - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); + return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); } catch (const nmos::control_protocol_exception& e) { slog::log(gate, SLOG_FLF) << "Set property: " << property_id.serialize() << " value: " << val.serialize() << " error: " << e.what(); - return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); + return details::make_nc_method_result({ nc_method_status::parameter_error }); } } // unknown property utility::stringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << " to do Set"; - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + return details::make_nc_method_result_error({ nc_method_status::property_not_implemented }, ss.str()); } // Get sequence item - web::json::value get_sequence_item(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + web::json::value get_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -115,28 +115,28 @@ namespace nmos // property is not a sequence utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); + return details::make_nc_method_result_error({ nc_method_status::invalid_request }, ss.str()); } if (data.as_array().size() > (size_t)index) { - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, data.at(index)); + return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, data.at(index)); } // out of bound utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do GetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); + return details::make_nc_method_result_error({ nc_method_status::index_out_of_bounds }, ss.str()); } // unknown property utility::stringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do GetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + return details::make_nc_method_result_error({ nc_method_status::property_not_implemented }, ss.str()); } // Set sequence item - web::json::value set_sequence_item(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + web::json::value set_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -153,7 +153,7 @@ namespace nmos { if (nmos::fields::nc::is_read_only(property)) { - return make_control_protocol_message_response(handle, { nc_method_status::read_only }); + return details::make_nc_method_result({ nc_method_status::read_only }); } auto& data = resource.data.at(nmos::fields::nc::name(property)); @@ -163,7 +163,7 @@ namespace nmos // property is not a sequence utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do SetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); + return details::make_nc_method_result_error({ nc_method_status::invalid_request }, ss.str()); } if (data.as_array().size() > (size_t)index) @@ -186,30 +186,30 @@ namespace nmos }, make_property_changed_event(nmos::fields::nc::oid(resource.data), { { property_id_, nc_property_change_type::type::sequence_item_changed, val, nc_id(index) } })); - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); + return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); } catch (const nmos::control_protocol_exception& e) { slog::log(gate, SLOG_FLF) << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize() << " error: " << e.what(); - return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); + return details::make_nc_method_result({ nc_method_status::parameter_error }); } } // out of bound utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do SetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); + return details::make_nc_method_result_error({ nc_method_status::index_out_of_bounds }, ss.str()); } // unknown property utility::stringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do SetSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + return details::make_nc_method_result_error({ nc_method_status::property_not_implemented }, ss.str()); } // Add item to sequence - web::json::value add_sequence_item(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + web::json::value add_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -227,7 +227,7 @@ namespace nmos { if (nmos::fields::nc::is_read_only(property)) { - return make_control_protocol_message_response(handle, { nc_method_status::read_only }); + return details::make_nc_method_result({ nc_method_status::read_only }); } if (!nmos::fields::nc::is_sequence(property)) @@ -235,7 +235,7 @@ namespace nmos // property is not a sequence utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do AddSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); + return details::make_nc_method_result_error({ nc_method_status::invalid_request }, ss.str()); } auto& data = resource.data.at(nmos::fields::nc::name(property)); @@ -262,24 +262,24 @@ namespace nmos }, make_property_changed_event(nmos::fields::nc::oid(resource.data), { { property_id_, nc_property_change_type::type::sequence_item_added, val, sequence_item_index } })); - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, sequence_item_index); + return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, value(sequence_item_index)); } catch (const nmos::control_protocol_exception& e) { slog::log(gate, SLOG_FLF) << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize() << " error: " << e.what(); - return make_control_protocol_message_response(handle, { nc_method_status::parameter_error }); + return details::make_nc_method_result({ nc_method_status::parameter_error }); } } // unknown property utility::stringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do AddSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + return details::make_nc_method_result_error({ nc_method_status::property_not_implemented }, ss.str()); } // Delete sequence item - web::json::value remove_sequence_item(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + web::json::value remove_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -299,7 +299,7 @@ namespace nmos // property is not a sequence utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do RemoveSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); + return details::make_nc_method_result_error({ nc_method_status::invalid_request }, ss.str()); } if (data.as_array().size() > (size_t)index) @@ -311,23 +311,23 @@ namespace nmos }, make_property_changed_event(nmos::fields::nc::oid(resource.data), { { details::parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_removed, nc_id(index) } })); - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); + return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); } // out of bound utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do RemoveSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::index_out_of_bounds }, ss.str()); + return details::make_nc_method_result_error({ nc_method_status::index_out_of_bounds }, ss.str()); } // unknown property utility::stringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do RemoveSequenceItem"); - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + return details::make_nc_method_result_error({ nc_method_status::property_not_implemented }, ss.str()); } // Get sequence length - web::json::value get_sequence_length(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + web::json::value get_sequence_length(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -346,7 +346,7 @@ namespace nmos // property is not a sequence utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceLength"); - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); + return details::make_nc_method_result_error({ nc_method_status::invalid_request }, ss.str()); } const auto& data = resource.data.at(nmos::fields::nc::name(property)); @@ -357,7 +357,7 @@ namespace nmos if (data.is_null()) { // null - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, value::null()); + return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, value::null()); } } else @@ -368,21 +368,21 @@ namespace nmos // null utility::stringstream_t ss; ss << U("property: ") << property_id.serialize() << " is a null sequence to do GetSequenceLength"; - return make_control_protocol_error_response(handle, { nc_method_status::invalid_request }, ss.str()); + return details::make_nc_method_result_error({ nc_method_status::invalid_request }, ss.str()); } } - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, uint32_t(data.as_array().size())); + return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, value(uint32_t(data.as_array().size()))); } // unknown property utility::stringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << " to do GetSequenceLength"; - return make_control_protocol_error_response(handle, { nc_method_status::property_not_implemented }, ss.str()); + return details::make_nc_method_result_error({ nc_method_status::property_not_implemented }, ss.str()); } // NcBlock methods implementation // Gets descriptors of members of the block - web::json::value get_member_descriptors(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + web::json::value get_member_descriptors(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -395,11 +395,11 @@ namespace nmos auto descriptors = value::array(); nmos::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); + return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); } // Finds member(s) by path - web::json::value find_members_by_path(nmos::resources& resources, const nmos::resource& resource_, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + web::json::value find_members_by_path(nmos::resources& resources, const nmos::resource& resource_, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -413,7 +413,7 @@ namespace nmos if (0 == path.size()) { // empty path - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty path to do FindMembersByPath")); + return details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("empty path to do FindMembersByPath")); } auto descriptors = value::array(); @@ -444,22 +444,22 @@ namespace nmos // no role utility::stringstream_t ss; ss << U("role: ") << role.as_string() << U(" not found to do FindMembersByPath"); - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, ss.str()); + return details::make_nc_method_result_error({ nc_method_status::parameter_error }, ss.str()); } } else { // no members - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("no members to do FindMembersByPath")); + return details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("no members to do FindMembersByPath")); } } web::json::push_back(descriptors, descriptor); - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); + return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); } // Finds members with given role name or fragment - web::json::value find_members_by_role(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + web::json::value find_members_by_role(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -475,17 +475,17 @@ namespace nmos if (role.empty()) { // empty role - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty role to do FindMembersByRole")); + return details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("empty role to do FindMembersByRole")); } auto descriptors = value::array(); nmos::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); + return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); } // Finds members with given class id - web::json::value find_members_by_class_id(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + web::json::value find_members_by_class_id(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -500,7 +500,7 @@ namespace nmos if (class_id.empty()) { // empty class_id - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty classId to do FindMembersByClassId")); + return details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("empty classId to do FindMembersByClassId")); } // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -508,12 +508,12 @@ namespace nmos auto descriptors = value::array(); nmos::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); + return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); } // NcClassManager methods implementation // Get a single class descriptor - web::json::value get_control_class(nmos::resources&, const nmos::resource&, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) + web::json::value get_control_class(nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate) { using web::json::value; @@ -525,7 +525,7 @@ namespace nmos if (class_id.empty()) { // empty class_id - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty classId to do GetControlClass")); + return details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("empty classId to do GetControlClass")); } // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -561,14 +561,14 @@ namespace nmos ? details::make_nc_class_descriptor(description, class_id, name, property_descriptors, method_descriptors, event_descriptors) : details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), property_descriptors, method_descriptors, event_descriptors); - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptor); + return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptor); } - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("classId not found")); + return details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("classId not found")); } // Get a single datatype descriptor - web::json::value get_datatype(nmos::resources&, const nmos::resource&, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler, slog::base_gate& gate) + web::json::value get_datatype(nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -580,7 +580,7 @@ namespace nmos if (name.empty()) { // empty name - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("empty name to do GetDatatype")); + return details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("empty name to do GetDatatype")); } const auto& datatype = get_control_protocol_datatype_descriptor(name); @@ -620,9 +620,9 @@ namespace nmos } } - return make_control_protocol_message_response(handle, { is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptor); + return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptor); } - return make_control_protocol_error_response(handle, { nc_method_status::parameter_error }, U("name not found")); + return details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("name not found")); } } diff --git a/Development/nmos/control_protocol_methods.h b/Development/nmos/control_protocol_methods.h index fe44d77c7..063d31125 100644 --- a/Development/nmos/control_protocol_methods.h +++ b/Development/nmos/control_protocol_methods.h @@ -13,35 +13,35 @@ namespace nmos { // NcObject methods implementation // Get property value - web::json::value get(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + web::json::value get(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // Set property value - web::json::value set(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + web::json::value set(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); // Get sequence item - web::json::value get_sequence_item(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + web::json::value get_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // Set sequence item - web::json::value set_sequence_item(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + web::json::value set_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); // Add item to sequence - web::json::value add_sequence_item(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + web::json::value add_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); // Delete sequence item - web::json::value remove_sequence_item(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + web::json::value remove_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // Get sequence length - web::json::value get_sequence_length(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + web::json::value get_sequence_length(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // NcBlock methods implementation // Get descriptors of members of the block - web::json::value get_member_descriptors(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + web::json::value get_member_descriptors(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // Finds member(s) by path - web::json::value find_members_by_path(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + web::json::value find_members_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // Finds members with given role name or fragment - web::json::value find_members_by_role(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + web::json::value find_members_by_role(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // Finds members with given class id - web::json::value find_members_by_class_id(nmos::resources& resources, const nmos::resource& resource, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + web::json::value find_members_by_class_id(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // NcClassManager methods implementation // Get a single class descriptor - web::json::value get_control_class(nmos::resources&, const nmos::resource&, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); + web::json::value get_control_class(nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler, slog::base_gate& gate); // Get a single datatype descriptor - web::json::value get_datatype(nmos::resources&, const nmos::resource&, int32_t handle, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype, control_protocol_property_changed_handler, slog::base_gate& gate); + web::json::value get_datatype(nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype, control_protocol_property_changed_handler, slog::base_gate& gate); } #endif diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 890e13631..6f9a83953 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -833,6 +833,15 @@ namespace nmos // message response // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#command-response-message-type + web::json::value make_control_protocol_response(int32_t handle, const web::json::value& method_result) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::handle, handle }, + { nmos::fields::nc::result, method_result } + }); + } web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message) { using web::json::value_of; diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 57fa13063..f41a07991 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -195,6 +195,7 @@ namespace nmos // message response // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#command-response-message-type + web::json::value make_control_protocol_response(int32_t handle, const web::json::value& method_result); web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message); web::json::value make_control_protocol_message_response(int32_t handle, const nc_method_result& method_result); web::json::value make_control_protocol_message_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value); // value can be sequence, NcClassDescriptor, NcDatatypeDescriptor diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index 30f15ab69..a9952690e 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -251,7 +251,7 @@ namespace nmos // get arguments const auto& arguments = nmos::fields::nc::arguments(cmd); - value response; + value nc_method_result; auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != resource) @@ -274,18 +274,19 @@ namespace nmos // execute the relevant method handler, then accumulating up their response to reponses if (standard_method) { - response = standard_method(resources, *resource, handle, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate); + // wrap the NcMethodResuls here + nc_method_result = standard_method(resources, *resource, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate); } else // non_standard_method { - response = non_standard_method(resources, *resource, handle, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), gate); + nc_method_result = non_standard_method(resources, *resource, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), gate); } } catch (const nmos::control_protocol_exception& e) { // invalid arguments slog::log(gate, SLOG_FLF) << "invalid argument: " << arguments.serialize() << " error: " << e.what(); - response = make_control_protocol_message_response(handle, { nmos::nc_method_status::parameter_error }); + nc_method_result = details::make_nc_method_result({ nmos::nc_method_status::parameter_error }); } } else @@ -294,7 +295,7 @@ namespace nmos utility::stringstream_t ss; ss << U("unsupported method_id: ") << nmos::fields::nc::method_id(cmd).serialize() << U(" for control class class_id: ") << resource->data.at(nmos::fields::nc::class_id).serialize(); - response = make_control_protocol_error_response(handle, { nc_method_status::method_not_implemented }, ss.str()); + nc_method_result = details::make_nc_method_result_error({ nc_method_status::method_not_implemented }, ss.str()); } } else @@ -302,9 +303,11 @@ namespace nmos // resource not found for the given oid utility::stringstream_t ss; ss << U("unknown oid: ") << oid; - response = make_control_protocol_error_response(handle, { nc_method_status::bad_oid }, ss.str()); + nc_method_result = details::make_nc_method_result_error({ nc_method_status::bad_oid }, ss.str()); } // accumulating up response + auto response = make_control_protocol_response(handle, nc_method_result); + web::json::push_back(responses, response); } From c0dc2f202689a0379284bf075c1e4114a936feb3 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 17 May 2024 15:32:04 +0100 Subject: [PATCH 111/250] Removed and renamed command message response helper functions --- .../nmos/control_protocol_resource.cpp | 37 +------------------ Development/nmos/control_protocol_resource.h | 8 +--- Development/nmos/control_protocol_ws_api.cpp | 2 +- 3 files changed, 5 insertions(+), 42 deletions(-) diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 6f9a83953..040de0328 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -831,7 +831,7 @@ namespace nmos } } - // message response + // command message response // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#command-response-message-type web::json::value make_control_protocol_response(int32_t handle, const web::json::value& method_result) { @@ -842,40 +842,7 @@ namespace nmos { nmos::fields::nc::result, method_result } }); } - web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::handle, handle }, - { nmos::fields::nc::result, details::make_nc_method_result_error(method_result, error_message) } - }); - } - web::json::value make_control_protocol_message_response(int32_t handle, const nc_method_result& method_result) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::handle, handle }, - { nmos::fields::nc::result, details::make_nc_method_result(method_result) } - }); - } - web::json::value make_control_protocol_message_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::handle, handle }, - { nmos::fields::nc::result, details::make_nc_method_result(method_result, value) } - }); - } - web::json::value make_control_protocol_message_response(int32_t handle, const nc_method_result& method_result, uint32_t value_) - { - using web::json::value; - - return make_control_protocol_message_response(handle, method_result, value(value_)); - } - web::json::value make_control_protocol_message_response(const web::json::value& responses) + web::json::value make_control_protocol_command_response(const web::json::value& responses) { using web::json::value_of; diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index f41a07991..5f341949b 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -193,14 +193,10 @@ namespace nmos web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state); } - // message response + // command message response // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#command-response-message-type web::json::value make_control_protocol_response(int32_t handle, const web::json::value& method_result); - web::json::value make_control_protocol_error_response(int32_t handle, const nc_method_result& method_result, const utility::string_t& error_message); - web::json::value make_control_protocol_message_response(int32_t handle, const nc_method_result& method_result); - web::json::value make_control_protocol_message_response(int32_t handle, const nc_method_result& method_result, const web::json::value& value); // value can be sequence, NcClassDescriptor, NcDatatypeDescriptor - web::json::value make_control_protocol_message_response(int32_t handle, const nc_method_result& method_result, uint32_t value); - web::json::value make_control_protocol_message_response(const web::json::value& responses); + web::json::value make_control_protocol_command_response(const web::json::value& responses); // subscription response // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#subscription-response-message-type diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index a9952690e..cfa382198 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -314,7 +314,7 @@ namespace nmos // add command_response to the grain ready to transfer to the client in nmos::send_control_protocol_ws_messages_thread resources.modify(grain, [&](nmos::resource& grain) { - web::json::push_back(nmos::fields::message_grain_data(grain.data), make_control_protocol_message_response(responses)); + web::json::push_back(nmos::fields::message_grain_data(grain.data), make_control_protocol_command_response(responses)); grain.updated = strictly_increasing_update(resources); }); From d2cca6d0e7747369942ac6fe82ae21d0647ff516 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe <64410119+jonathan-r-thorpe@users.noreply.github.com> Date: Mon, 20 May 2024 11:38:56 +0100 Subject: [PATCH 112/250] Apply suggestions from code review Co-authored-by: Simon Lo --- Development/nmos/control_protocol_methods.cpp | 2 +- Development/nmos/control_protocol_resource.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index 7106b0e25..446a47492 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -262,7 +262,7 @@ namespace nmos }, make_property_changed_event(nmos::fields::nc::oid(resource.data), { { property_id_, nc_property_change_type::type::sequence_item_added, val, sequence_item_index } })); - return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, value(sequence_item_index)); + return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, sequence_item_index); } catch (const nmos::control_protocol_exception& e) { diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 040de0328..83ccaad08 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -840,7 +840,7 @@ namespace nmos return value_of({ { nmos::fields::nc::handle, handle }, { nmos::fields::nc::result, method_result } - }); + }); } web::json::value make_control_protocol_command_response(const web::json::value& responses) { From 59da8c50bf274c9437d9e7cfe23215eae93c7e93 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Tue, 21 May 2024 16:48:36 +0100 Subject: [PATCH 113/250] Added Device configuration data type definitions. --- .../nmos/control_protocol_resource.cpp | 70 +++++++++++++++++++ Development/nmos/control_protocol_resource.h | 15 ++++ Development/nmos/control_protocol_state.cpp | 10 ++- Development/nmos/json_fields.h | 3 + 4 files changed, 97 insertions(+), 1 deletion(-) diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 83ccaad08..1e9deebac 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -2049,4 +2049,74 @@ namespace nmos web::json::push_back(items, details::make_nc_enum_item_descriptor(U("A payload error was encountered"), U("PayloadError"), 3)); return details::make_nc_datatype_descriptor_enum(U("Connection status enum data typee"), U("NcPayloadStatus"), items, value::null()); } + + // Device Configuration datatypes + // TODO: add link + web::json::value make_nc_property_value_holder_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property type name. If null it means the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Is the property ReadOnly?"), nmos::fields::nc::is_read_only, U("NcBoolean"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); + + return details::make_nc_datatype_descriptor_struct(U("Property value holder descriptor"), U("NcPropertyValueHolder"), fields, value::null()); + } + // TODO: add link + web::json::value make_nc_object_properties_holder_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Object role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Object properties values"), nmos::fields::nc::values, U("NcPropertyValueHolder"), false, true, value::null())); + + return details::make_nc_datatype_descriptor_struct(U("Object properties holder descriptor"), U("NcObjectPropertiesHolder"), fields, value::null()); + } + // TODO: add link + web::json::value make_nc_bulk_values_holder_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional vendor specific fingerprinting mechanism used for validation purposes"), nmos::fields::nc::validation_fingerprint, U("NcString"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Values by rolePath"), nmos::fields::nc::values, U("NcObjectPropertiesHolder"), false, true, value::null())); + + return details::make_nc_datatype_descriptor_struct(U("Bulk values holder descriptor"), U("NcBulkValuesHolder"), fields, value::null()); + } + // TODO: add link + web::json::value make_nc_object_properties_set_validation_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Object role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation status"), nmos::fields::nc::status, U("NcMethodStatus"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation status message"), nmos::fields::nc::status_message, U("NcString"), true, false, value::null())); + + return details::make_nc_datatype_descriptor_struct(U("Object properties set validation descriptor"), U("NcObjectPropertiesSetValidation"), fields, value::null()); + } + // TODO: add link + web::json::value make_nc_method_result_bulk_values_holder_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Bulk values holder value"), nmos::fields::nc::value, U("NcBulkValuesHolder"), false, false, value::null())); + + return details::make_nc_datatype_descriptor_struct(U("Method result containing bulk values holder descriptor"), U("NcMethodResultBulkValuesHolder"), fields, U("NcMethodResult"), value::null()); + } + // TODO: add link + web::json::value make_nc_method_result_object_properties_set_validation_datatype() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Object properties set path validation"), nmos::fields::nc::value, U("NcObjectPropertiesSetValidation"), false, true, value::null())); + + return details::make_nc_datatype_descriptor_struct(U("Method result containing object properties set validation descriptor"), U("NcMethodResultObjectPropertiesSetValidation"), fields, U("NcMethodResult"), value::null()); + } } diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 5f341949b..5994f8366 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -428,6 +428,21 @@ namespace nmos web::json::value make_nc_connection_status_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncpayloadstatus web::json::value make_nc_payload_status_datatype(); + + // Device configuration feature set datatypes + // TODO: add link + // + web::json::value make_nc_property_value_holder_datatype(); + // + web::json::value make_nc_object_properties_holder_datatype(); + // + web::json::value make_nc_bulk_values_holder_datatype(); + // + web::json::value make_nc_object_properties_set_validation_datatype(); + // + web::json::value make_nc_method_result_bulk_values_holder_datatype(); + // + web::json::value make_nc_method_result_object_properties_set_validation_datatype(); } #endif diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index b611c2ed1..16cf9340c 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -300,7 +300,15 @@ namespace nmos // Monitoring feature set // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#datatypes { U("NcConnectionStatus"), {make_nc_connection_status_datatype()} }, - { U("NcPayloadStatus"), {make_nc_payload_status_datatype()} } + { U("NcPayloadStatus"), {make_nc_payload_status_datatype()} }, + // Device configuration feature set + // TODO: add link + { U("NcPropertyValueHolder"), {make_nc_property_value_holder_datatype()}}, + { U("NcObjectPropertiesHolder"), {make_nc_object_properties_holder_datatype()}}, + { U("NcBulkValuesHolder"), {make_nc_bulk_values_holder_datatype()}}, + { U("NcObjectPropertiesSetValidation"), {make_nc_object_properties_set_validation_datatype()}}, + { U("NcMethodResultBulkValuesHolder"), {make_nc_method_result_bulk_values_holder_datatype()}}, + { U("NcMethodResultObjectPropertiesSetValidation"), {make_nc_method_result_object_properties_set_validation_datatype()}} }; } diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index 7374c5a90..325fa0a82 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -336,6 +336,9 @@ namespace nmos const web::json::field_as_string payload_status_message{ U("payloadStatusMessage") }; const web::json::field_as_bool signal_protection_status{ U("signalProtectionStatus") }; const web::json::field_as_bool active{ U("active") }; + const web::json::field_as_value values{ U("values") }; + const web::json::field_as_value validation_fingerprint{ U("validationFingerprint") }; + const web::json::field_as_value status_message{ U("statusMessage") }; } // NMOS Parameter Registers From 8f24340ccb2d7c85614ba58250262e48c277a986 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 28 May 2024 20:32:21 +0100 Subject: [PATCH 114/250] Remove not in use code, thanks for @maweit suggestion --- Development/nmos/configuration_api.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 8d9481f0a..50eb1b951 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -58,13 +58,6 @@ namespace nmos namespace details { - namespace fields - { - const web::json::field_as_string_or level{ U("level"), {} }; - const web::json::field_as_string_or index{ U("index"), {} }; - const web::json::field_as_string_or describe{ U("describe"), {} }; - } - void build_role_paths(const resources& resources, const nmos::resource& resource, const utility::string_t& base_role_path, std::set& role_paths) { if (resource.data.has_field(nmos::fields::nc::members)) From 173af96832e9ac7cf09e9a390b54345ed35438df Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe <64410119+jonathan-r-thorpe@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:58:09 +0100 Subject: [PATCH 115/250] Update Development/nmos/json_fields.h Co-authored-by: Simon Lo --- Development/nmos/json_fields.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index 325fa0a82..6a1fc6a56 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -336,9 +336,9 @@ namespace nmos const web::json::field_as_string payload_status_message{ U("payloadStatusMessage") }; const web::json::field_as_bool signal_protection_status{ U("signalProtectionStatus") }; const web::json::field_as_bool active{ U("active") }; - const web::json::field_as_value values{ U("values") }; - const web::json::field_as_value validation_fingerprint{ U("validationFingerprint") }; - const web::json::field_as_value status_message{ U("statusMessage") }; + const web::json::field_as_array values{ U("values") }; + const web::json::field_as_string validation_fingerprint{ U("validationFingerprint") }; + const web::json::field_as_string status_message{ U("statusMessage") }; } // NMOS Parameter Registers From 976d7666c00475a30ce80ba716289498898a8b67 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 5 Jun 2024 17:42:46 +0100 Subject: [PATCH 116/250] Modify using the updated version of the control method handler --- Development/nmos/configuration_api.cpp | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 50eb1b951..7b626613e 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -531,28 +531,20 @@ namespace nmos if (resources.end() != resource) { auto method = get_control_protocol_method_descriptor(details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), details::parse_formatted_method_id(method_id)); - auto& nc_method_descriptor = std::get<0>(method); - auto& standard_method = std::get<1>(method); - auto& non_standard_method = std::get<2>(method); + auto& nc_method_descriptor = method.first; + auto& control_method_handler = method.second; web::http::status_code code{ status_codes::BadRequest }; value method_result; - if (standard_method || non_standard_method) + if (control_method_handler) { try { // do method arguments constraints validation method_parameters_contraints_validation(arguments, nc_method_descriptor, get_control_protocol_datatype_descriptor); - // execute the relevant method handler, then accumulating up their response to reponses - if (standard_method) - { - method_result = standard_method(resources, *resource, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate); - } - else // non_standard_method - { - method_result = non_standard_method(resources, *resource, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), gate); - } + // execute the relevant control method handler, then accumulating up their response to reponses + method_result = control_method_handler(resources, *resource, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), gate); auto status = nmos::fields::nc::status(method_result); if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } From 2588cfba43f8291b37c87ca57cc0fbb307e09cd3 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe <64410119+jonathan-r-thorpe@users.noreply.github.com> Date: Wed, 19 Jun 2024 16:33:13 +0100 Subject: [PATCH 117/250] Add IS-14 bulkPropertiesManager object and endpoints (#7) * Add bulkPropertiesManager control class * Add fixed role to bulkPropertiesManger * Add fixed role and correct method_id in bulkPropertiesManager * add Device Configuration method handlers * Device Configuration method handlers in NcBulkPropertiesManager method * Add bulkProperties endpoints * Typedefs for bulk properties manager user defined methods * Schema validation on bulkProperties endpoint * Pass control protocol resources required for backup/restore --------- Co-authored-by: Simon Lo --- Development/nmos-cpp-node/main.cpp | 2 +- .../nmos-cpp-node/node_implementation.cpp | 44 ++++- Development/nmos/configuration_api.cpp | 154 ++++++++++++++++-- Development/nmos/configuration_api.h | 2 +- Development/nmos/control_protocol_handlers.h | 6 + .../nmos/control_protocol_resource.cpp | 64 ++++++++ Development/nmos/control_protocol_resource.h | 9 + .../nmos/control_protocol_resources.cpp | 12 ++ Development/nmos/control_protocol_resources.h | 5 + Development/nmos/control_protocol_state.cpp | 88 +++++++++- Development/nmos/control_protocol_state.h | 3 +- Development/nmos/control_protocol_typedefs.h | 7 + Development/nmos/json_fields.h | 2 + Development/nmos/json_schema.cpp | 24 +-- Development/nmos/json_schema.h | 8 +- Development/nmos/node_server.cpp | 2 +- Development/nmos/node_server.h | 13 +- .../nmos/test/control_protocol_test.cpp | 4 +- Development/nmos/type.h | 1 + 19 files changed, 414 insertions(+), 36 deletions(-) diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index e4b420fa2..51a7dec13 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -138,7 +138,7 @@ int main(int argc, char* argv[]) .on_request_authorization_code(nmos::experimental::make_request_authorization_code_handler(gate)); // may be omitted, only required for OAuth client which is using the Authorization Code Flow to obtain the access token } - nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed); + nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.get_properties_by_path, node_implementation.validate_set_properties_by_path, node_implementation.set_properties_by_path); if (0 <= nmos::fields::control_protocol_ws_port(node_model.settings)) { node_implementation diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index f12cca15e..33a6251df 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1206,6 +1206,9 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // example class manager auto class_manager = nmos::make_class_manager(++oid, control_protocol_state); + // example bulk properties manager + auto bulk_properties_manager = nmos::make_bulk_properties_manager(++oid); + // example stereo gain const auto stereo_gain_oid = ++oid; auto stereo_gain = nmos::make_block(stereo_gain_oid, nmos::root_block_oid, U("stereo-gain"), U("Stereo gain"), U("Stereo gain block")); @@ -1714,6 +1717,42 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control }; } +// 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) +{ + return [&resources, &gate](const nmos::resource& resource, bool recurse) + { + slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do get_properties_by_path"; + + // Implement backup of device model here + return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }); + }; +} + +// 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) +{ + return [&resources, &gate](const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse) + { + slog::log(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 }); + }; +} + +// 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](const nmos::resource& resource, const web::json::value& data_set, bool recurse, bool allow_incomplete) + { + slog::log(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 }); + }; +} + namespace impl { nmos::interlace_mode get_interlace_mode(const nmos::settings& settings) @@ -1868,5 +1907,8 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode .on_connection_activated(make_node_implementation_connection_activation_handler(model, gate)) .on_validate_channelmapping_output_map(make_node_implementation_map_validator()) // may be omitted if not required .on_channelmapping_activated(make_node_implementation_channelmapping_activation_handler(gate)) - .on_control_protocol_property_changed(make_node_implementation_control_protocol_property_changed_handler(gate)); // may be omitted if IS-12 not required + .on_control_protocol_property_changed(make_node_implementation_control_protocol_property_changed_handler(gate)) // may be omitted if IS-12 not required + .on_get_properties_by_path(make_node_implementation_get_properties_by_path_handler(model.control_protocol_resources, gate)) // may be omitted if IS-14 not required + .on_validate_set_properties_by_path(make_node_implementation_validate_set_properties_by_path_handler(model.control_protocol_resources, gate)) // may be omitted if IS-14 not required + .on_set_properties_by_path(make_node_implementation_set_properties_by_path_handler(model.control_protocol_resources, gate)); // may be omitted if IS-14 not required } diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 7b626613e..3875bab33 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -18,9 +18,9 @@ namespace nmos { - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_properties_by_path_handler get_properties_by_path, validate_set_properties_by_path_handler validate_set_properties_by_path, set_properties_by_path_handler set_properties_by_path, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_properties_by_path_handler get_properties_by_path, validate_set_properties_by_path_handler validate_set_properties_by_path, set_properties_by_path_handler set_properties_by_path, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -51,7 +51,7 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, property_changed, gate)); + configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, get_properties_by_path, validate_set_properties_by_path, set_properties_by_path, property_changed, gate)); return configuration_api; } @@ -205,16 +205,29 @@ namespace nmos static const web::json::experimental::json_validator validator { nmos::experimental::load_json_schema, - boost::copy_range>(boost::range::join( - is14_versions::all | boost::adaptors::transformed(experimental::make_configrationapi_method_patch_request_schema_uri), - is14_versions::all | boost::adaptors::transformed(experimental::make_configrationapi_property_value_put_request_schema_uri) - )) + boost::copy_range>(boost::range::join(boost::range::join(boost::range::join( + is14_versions::all | boost::adaptors::transformed(experimental::make_configurationapi_method_patch_request_schema_uri), + is14_versions::all | boost::adaptors::transformed(experimental::make_configurationapi_property_value_put_request_schema_uri)), + is14_versions::all | boost::adaptors::transformed(experimental::make_configurationapi_bulkProperties_validate_request_schema_uri)), + is14_versions::all | boost::adaptors::transformed(experimental::make_configurationapi_bulkProperties_set_request_schema_uri))) }; return validator; } + + bool parse_recurse_query_parameter(const utility::string_t& query) + { + web::json::value arguments = web::json::value_from_query(query); + + if (arguments.has_boolean_field(fields::nc::recurse)) + { + return fields::nc::recurse(arguments); + } + + return true; + } } - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_properties_by_path_handler get_properties_by_path, validate_set_properties_by_path_handler validate_set_properties_by_path, set_properties_by_path_handler set_properties_by_path, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -519,7 +532,7 @@ namespace nmos const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); // Validate JSON syntax according to the schema - details::configurationapi_validator().validate(body, experimental::make_configrationapi_method_patch_request_schema_uri(version)); + details::configurationapi_validator().validate(body, experimental::make_configurationapi_method_patch_request_schema_uri(version)); const auto role_path = parameters.at(nmos::patterns::rolePath.name); const auto method_id = parameters.at(nmos::patterns::methodId.name); @@ -594,7 +607,7 @@ namespace nmos const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); // Validate JSON syntax according to the schema - details::configurationapi_validator().validate(body, experimental::make_configrationapi_property_value_put_request_schema_uri(version)); + details::configurationapi_validator().validate(body, experimental::make_configurationapi_property_value_put_request_schema_uri(version)); const auto role_path = parameters.at(nmos::patterns::rolePath.name); const auto property_id = parameters.at(nmos::patterns::propertyId.name); @@ -634,6 +647,127 @@ namespace nmos }); }); + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::GET, [&model, get_properties_by_path, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + + auto lock = model.read_lock(); + + auto& resources = model.control_protocol_resources; + + const auto& resource = details::find_resource(resources, role_path); + if (resources.end() != resource) + { + bool recurse = details::parse_recurse_query_parameter(req.request_uri().query()); + + auto result = details::make_nc_method_result_error({ nc_method_status::method_not_implemented }, U("get_properties_by_path not provided")); + if (get_properties_by_path) + { + result = get_properties_by_path(*resource, recurse); + } + + auto status = nmos::fields::nc::status(result); + auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; + set_reply(res, code, result); + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return pplx::task_from_result(true); + }); + + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, validate_set_properties_by_path, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + + auto lock = model.read_lock(); + const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); + auto& resources = model.control_protocol_resources; + + const auto& resource = details::find_resource(resources, role_path); + if (resources.end() != resource) + { + return details::extract_json(req, gate_).then([res, resources, resource, validate_set_properties_by_path, version, &gate_](value body) mutable + { + // Validate JSON syntax according to the schema + details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_validate_request_schema_uri(version)); + + bool recurse = nmos::fields::nc::recurse(body); + const auto& data_set = nmos::fields::nc::data_set(body); + if (!data_set.is_null()) + { + auto result = details::make_nc_method_result_error({ nc_method_status::method_not_implemented }, U("validate_set_properties_by_path not provided")); + if (validate_set_properties_by_path) + { + result = validate_set_properties_by_path(*resource, data_set, recurse); + } + + auto status = nmos::fields::nc::status(result); + auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; + set_reply(res, code, result); + } + else + { + set_reply(res, status_codes::BadRequest, nmos::details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("Null dataSet parameter"))); + } + return true; + }); + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return pplx::task_from_result(true); + }); + + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, set_properties_by_path, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + + auto lock = model.read_lock(); + const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); + auto& resources = model.control_protocol_resources; + + const auto& resource = details::find_resource(resources, role_path); + if (resources.end() != resource) + { + return details::extract_json(req, gate_).then([res, resources, resource, set_properties_by_path, version, &gate_](value body) mutable + { + // Validate JSON syntax according to the schema + details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_set_request_schema_uri(version)); + + const auto& arguments = nmos::fields::nc::arguments(body); + bool recurse = nmos::fields::nc::recurse(arguments); + bool allow_incomplete = nmos::fields::nc::allow_incomplete(arguments); + const auto& data_set = nmos::fields::nc::data_set(arguments); + + auto result = details::make_nc_method_result_error({ nc_method_status::method_not_implemented }, U("set_properties_by_path not provided")); + if (set_properties_by_path) + { + result = set_properties_by_path(*resource, data_set, recurse, allow_incomplete); + } + + auto status = nmos::fields::nc::status(result); + auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; + set_reply(res, code, result); + + return true; + }); + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + + return pplx::task_from_result(true); + }); + return configuration_api; } } diff --git a/Development/nmos/configuration_api.h b/Development/nmos/configuration_api.h index 9b1a73297..6e4b8f081 100644 --- a/Development/nmos/configuration_api.h +++ b/Development/nmos/configuration_api.h @@ -15,7 +15,7 @@ namespace nmos { struct node_model; - web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_properties_by_path_handler get_properties_by_path, validate_set_properties_by_path_handler validate_set_properties_by_path, set_properties_by_path_handler set_properties_by_path, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); } #endif diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index 0dcd84787..e7d5fda03 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -36,6 +36,12 @@ namespace nmos // this callback should not throw exceptions, as the relevant property will already has been changed and those changes will not be rolled back typedef std::function control_protocol_property_changed_handler; + // Device Configuration handlers + // these callbacks should not throw exceptions + typedef std::function get_properties_by_path_handler; + typedef std::function validate_set_properties_by_path_handler; + typedef std::function set_properties_by_path_handler; + namespace experimental { // control method handler definition diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 1e9deebac..9c16334cb 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -817,6 +817,16 @@ namespace nmos return data; } + // TODO: add link + web::json::value make_nc_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value &user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + { + using web::json::value; + + auto data = make_nc_manager(nc_bulk_properties_manager_class_id, oid, true, owner, U("BulkPropertiesManager"), user_label, description, touchpoints, runtime_property_constraints); + + return data; + } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertychangedeventdata web::json::value make_nc_property_changed_event_data(const nc_property_changed_event_data& property_changed_event_data) { @@ -1227,6 +1237,51 @@ namespace nmos return value::array(); } + // Device configuration classes + // NcBulkPropertiesManager + // TODO: add link + web::json::value make_nc_bulk_properties_manager_properties() + { + using web::json::value; + + return value::array(); + } + web::json::value make_nc_bulk_properties_manager_methods() + { + using web::json::value; + + auto methods = value::array(); + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The target role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will return properties on specified path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get bulk object properties by given path"), nc_bulk_properties_manager_get_properties_by_path_method_id, U("GetPropertiesByPath"), U("NcMethodResultBulkValuesHolder"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkValuesHolder"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Validate bulk properties for setting by given paths"), nc_bulk_properties_manager_validate_set_properties_by_path_method_id, U("ValidateSetPropertiesByPath"), U("NcMethodResultObjectPropertiesSetValidation"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkValuesHolder"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will allow the device to restore only the role paths which pass validation(perform an incomplete restore)"), nmos::fields::nc::allow_incomplete, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Set bulk properties for setting by given paths"), nc_bulk_properties_manager_set_properties_by_path_method_id, U("SetPropertiesByPath"), U("NcMethodResultObjectPropertiesSetValidation"), parameters, false)); + } + + return methods; + } + web::json::value make_nc_bulk_properties_manager_events() + { + using web::json::value; + + return value::array(); + } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.html web::json::value make_nc_object_class() { @@ -1301,6 +1356,15 @@ namespace nmos return details::make_nc_class_descriptor(U("NcReceiverMonitorProtected class descriptor"), nc_receiver_monitor_protected_class_id, U("NcReceiverMonitorProtected"), make_nc_receiver_monitor_protected_properties(), make_nc_receiver_monitor_protected_methods(), make_nc_receiver_monitor_protected_events()); } + // Device configuration feature set control classes + // TODO: add link + web::json::value make_nc_bulk_properties_manager_class() + { + using web::json::value; + + return details::make_nc_class_descriptor(U("NcBulkPropertiesManager class descriptor"), nc_bulk_properties_manager_class_id, U("NcBulkPropertiesManager"), U("BulkPropertiesManager"), make_nc_bulk_properties_manager_properties(), make_nc_bulk_properties_manager_methods(), make_nc_bulk_properties_manager_events()); + } + // Primitive datatypes // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives web::json::value make_nc_boolean_datatype() diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 5994f8366..37948dc4e 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -191,6 +191,9 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state); + + // TODO: add link + web::json::value make_nc_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); } // command message response @@ -281,6 +284,12 @@ namespace nmos web::json::value make_nc_ident_beacon_methods(); web::json::value make_nc_ident_beacon_events(); + // Device configuration classes + // TODO: add link + web::json::value make_nc_bulk_properties_manager_properties(); + web::json::value make_nc_bulk_properties_manager_methods(); + web::json::value make_nc_bulk_properties_manager_events(); + // Datatype models // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/#datatype-models-for-branch-v10-dev // diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index 26030a6f1..485379b09 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -98,4 +98,16 @@ namespace nmos return{ is12_versions::v1_0, types::nc_ident_beacon, std::move(data), true }; } + + // Device Configuration feature set control classes + // + // TODO: add link + control_protocol_resource make_bulk_properties_manager(nc_oid oid) + { + using web::json::value; + + auto data = details::make_nc_bulk_properties_manager(oid, root_block_oid, value::string(U("Bulk properties manager")), U("The bulk properties manager offers a central model for getting and setting properties of multiple role paths"), value::null(), value::null()); + + return{ is12_versions::v1_0, types::nc_bulk_properties_manager, std::move(data), true }; + } } diff --git a/Development/nmos/control_protocol_resources.h b/Development/nmos/control_protocol_resources.h index 4ad7d85da..7e29fa13b 100644 --- a/Development/nmos/control_protocol_resources.h +++ b/Development/nmos/control_protocol_resources.h @@ -49,6 +49,11 @@ namespace nmos control_protocol_resource make_ident_beacon(nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), bool enabled = true, bool active = false ); + + // Device Configuration feature set control classes + // + // create Bulk Properties Manager resource + control_protocol_resource make_bulk_properties_manager(nc_oid oid); } #endif diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 3ef1424b5..e4258ea08 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -1,5 +1,6 @@ #include "nmos/control_protocol_state.h" +#include "cpprest/http_utils.h" #include "nmos/control_protocol_methods.h" #include "nmos/control_protocol_resource.h" @@ -178,9 +179,84 @@ namespace nmos return get_datatype(resources, resource, arguments, is_deprecated, get_control_protocol_datatype_descriptor, gate); }; } + nmos::experimental::control_protocol_method_handler make_nc_get_properties_by_path_handler(get_properties_by_path_handler get_properties_by_path) + { + return [get_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + { + bool recurse = nmos::fields::nc::recurse(arguments); + + // Delegate to user defined handler + + auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); + if (get_properties_by_path) + { + result = get_properties_by_path(resource, recurse); + + const auto& status = nmos::fields::nc::status(result); + if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) + { + return nmos::details::make_nc_method_result({ nmos::nc_method_status::method_deprecated }, nmos::fields::nc::value(result)); + } + } + return result; + }; + } + nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(validate_set_properties_by_path_handler validate_set_properties_by_path) + { + return [validate_set_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + { + bool recurse = nmos::fields::nc::recurse(arguments); + const auto& data_set = nmos::fields::nc::data_set(arguments); + + if (data_set.is_null()) + { + return nmos::details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("Null dataSet parameter")); + } + + auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); + if (validate_set_properties_by_path) + { + result = validate_set_properties_by_path(resource, data_set, recurse); + + const auto& status = nmos::fields::nc::status(result); + if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) + { + return nmos::details::make_nc_method_result({ nmos::nc_method_status::method_deprecated }, nmos::fields::nc::value(result)); + } + } + return result; + }; + } + nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(set_properties_by_path_handler set_properties_by_path) + { + return [set_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + { + bool recurse = nmos::fields::nc::recurse(arguments); + bool allow_incomplete = nmos::fields::nc::allow_incomplete(arguments); + const auto& data_set = nmos::fields::nc::data_set(arguments); + + if (data_set.is_null()) + { + return nmos::details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("Null dataSet parameter")); + } + + auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); + if (set_properties_by_path) + { + result = set_properties_by_path(resource, data_set, recurse, allow_incomplete); + + const auto& status = nmos::fields::nc::status(result); + if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) + { + return nmos::details::make_nc_method_result({ nmos::nc_method_status::method_deprecated }, nmos::fields::nc::value(result)); + } + } + return result; + }; + } } - control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed) + control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, get_properties_by_path_handler get_properties_by_path_handler, validate_set_properties_by_path_handler validate_set_properties_by_path_handler, set_properties_by_path_handler set_properties_by_path_handler) { using web::json::value; @@ -314,6 +390,16 @@ namespace nmos // NcReceiverMonitorProtected methods to_methods_vector(make_nc_receiver_monitor_protected_methods(), {}), // NcReceiverMonitorProtected events + to_vector(make_nc_receiver_monitor_protected_events())) }, + // NcBulkPropertiesManager + { nc_bulk_properties_manager_class_id, make_control_class_descriptor(U("NcBulkPropertiesManager class descriptor"), nc_bulk_properties_manager_class_id, U("NcBulkPropertiesManager"), U("BulkPropertiesManager"), + to_vector(make_nc_bulk_properties_manager_properties()), + to_methods_vector(make_nc_bulk_properties_manager_methods(), + { + { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(get_properties_by_path_handler) }, + { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(validate_set_properties_by_path_handler) }, + { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(set_properties_by_path_handler) } + }), to_vector(make_nc_receiver_monitor_protected_events())) } }; diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index b49d4e502..bb149bd1d 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -58,8 +58,7 @@ namespace nmos nmos::read_lock read_lock() const { return nmos::read_lock{ mutex }; } nmos::write_lock write_lock() const { return nmos::write_lock{ mutex }; } - control_protocol_state(control_protocol_property_changed_handler property_changed); - + control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, get_properties_by_path_handler get_properties_by_path_handler = nullptr, validate_set_properties_by_path_handler validate_set_properties_by_path_handler = nullptr, set_properties_by_path_handler set_properties_by_path_handler = nullptr); // insert control class descriptor, false if class descriptor already inserted bool insert(const experimental::control_class_descriptor& control_class_descriptor); // erase control class of the given class id, false if the required class not found diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index 541e868ca..e65b03c47 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -171,6 +171,11 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager const nc_method_id nc_class_manager_get_control_class_method_id(3, 1); const nc_method_id nc_class_manager_get_datatype_method_id(3, 2); + // NcMethodsIds for NcBulkPropertiesManager + // TODO: add link + const nc_method_id nc_bulk_properties_manager_get_properties_by_path_method_id(3, 1); + const nc_method_id nc_bulk_properties_manager_validate_set_properties_by_path_method_id(3, 2); + const nc_method_id nc_bulk_properties_manager_set_properties_by_path_method_id(3, 3); // NcPropertyId // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyid @@ -270,6 +275,8 @@ namespace nmos const nc_class_id nc_receiver_monitor_class_id({ 1, 2, 3 }); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitorprotected const nc_class_id nc_receiver_monitor_protected_class_id({ 1, 2, 3, 1 }); + // TODO: add link + const nc_class_id nc_bulk_properties_manager_class_id({ 1, 3, 3 }); // NcTouchpoint // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpoint diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index 6a1fc6a56..dc000c836 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -339,6 +339,8 @@ namespace nmos const web::json::field_as_array values{ U("values") }; const web::json::field_as_string validation_fingerprint{ U("validationFingerprint") }; const web::json::field_as_string status_message{ U("statusMessage") }; + const web::json::field_as_value data_set{ U("dataSet") }; // NcBulkValuesHolder + const web::json::field_as_bool allow_incomplete{ U("allowIncomplete") }; } // NMOS Parameter Registers diff --git a/Development/nmos/json_schema.cpp b/Development/nmos/json_schema.cpp index 99220aab5..81bff63b4 100644 --- a/Development/nmos/json_schema.cpp +++ b/Development/nmos/json_schema.cpp @@ -186,10 +186,10 @@ namespace nmos using namespace nmos::is14_schemas::v1_0_x; const utility::string_t tag(_XPLATSTR("v1.0.x")); - const web::uri configrationapi_bulkProperties_set_request_schema_uri = make_schema_uri(tag, _XPLATSTR("bulkProperties-set-request.json")); - const web::uri configrationapi_bulkProperties_validate_request_schema_uri = make_schema_uri(tag, _XPLATSTR("bulkProperties-validate-request.json")); - const web::uri configrationapi_method_patch_request_schema_uri = make_schema_uri(tag, _XPLATSTR("method-patch-request.json")); - const web::uri configrationapi_property_value_put_request_schema_uri = make_schema_uri(tag, _XPLATSTR("property-value-put-request.json")); + const web::uri configurationapi_bulkProperties_set_request_schema_uri = make_schema_uri(tag, _XPLATSTR("bulkProperties-set-request.json")); + const web::uri configurationapi_bulkProperties_validate_request_schema_uri = make_schema_uri(tag, _XPLATSTR("bulkProperties-validate-request.json")); + const web::uri configurationapi_method_patch_request_schema_uri = make_schema_uri(tag, _XPLATSTR("method-patch-request.json")); + const web::uri configurationapi_property_value_put_request_schema_uri = make_schema_uri(tag, _XPLATSTR("property-value-put-request.json")); } } } @@ -547,24 +547,24 @@ namespace nmos return is12_schemas::v1_0::controlprotocolapi_subscription_message_schema_uri; } - web::uri make_configrationapi_bulkProperties_set_request_schema_uri(const nmos::api_version& version) + web::uri make_configurationapi_bulkProperties_set_request_schema_uri(const nmos::api_version& version) { - return is14_schemas::v1_0::configrationapi_bulkProperties_set_request_schema_uri; + return is14_schemas::v1_0::configurationapi_bulkProperties_set_request_schema_uri; } - web::uri make_configrationapi_bulkProperties_validate_request_schema_uri(const nmos::api_version& version) + web::uri make_configurationapi_bulkProperties_validate_request_schema_uri(const nmos::api_version& version) { - return is14_schemas::v1_0::configrationapi_bulkProperties_validate_request_schema_uri; + return is14_schemas::v1_0::configurationapi_bulkProperties_validate_request_schema_uri; } - web::uri make_configrationapi_method_patch_request_schema_uri(const nmos::api_version& version) + web::uri make_configurationapi_method_patch_request_schema_uri(const nmos::api_version& version) { - return is14_schemas::v1_0::configrationapi_method_patch_request_schema_uri; + return is14_schemas::v1_0::configurationapi_method_patch_request_schema_uri; } - web::uri make_configrationapi_property_value_put_request_schema_uri(const nmos::api_version& version) + web::uri make_configurationapi_property_value_put_request_schema_uri(const nmos::api_version& version) { - return is14_schemas::v1_0::configrationapi_property_value_put_request_schema_uri; + return is14_schemas::v1_0::configurationapi_property_value_put_request_schema_uri; } // load the json schema for the specified base URI diff --git a/Development/nmos/json_schema.h b/Development/nmos/json_schema.h index 6c98d8a66..d1a06be60 100644 --- a/Development/nmos/json_schema.h +++ b/Development/nmos/json_schema.h @@ -40,10 +40,10 @@ namespace nmos web::uri make_controlprotocolapi_command_message_schema_uri(const nmos::api_version& version); web::uri make_controlprotocolapi_subscription_message_schema_uri(const nmos::api_version& version); - web::uri make_configrationapi_bulkProperties_set_request_schema_uri(const nmos::api_version& version); - web::uri make_configrationapi_bulkProperties_validate_request_schema_uri(const nmos::api_version& version); - web::uri make_configrationapi_method_patch_request_schema_uri(const nmos::api_version& version); - web::uri make_configrationapi_property_value_put_request_schema_uri(const nmos::api_version& version); + web::uri make_configurationapi_bulkProperties_set_request_schema_uri(const nmos::api_version& version); + web::uri make_configurationapi_bulkProperties_validate_request_schema_uri(const nmos::api_version& version); + web::uri make_configurationapi_method_patch_request_schema_uri(const nmos::api_version& version); + web::uri make_configurationapi_property_value_put_request_schema_uri(const nmos::api_version& version); // load the json schema for the specified base URI web::json::value load_json_schema(const web::uri& id); diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index 1e9a97da2..c665a828b 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -77,7 +77,7 @@ namespace nmos // Configure the Configuration API - node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.control_protocol_property_changed, gate)); + node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.get_properties_by_path, node_implementation.validate_set_properties_by_path, node_implementation.set_properties_by_path, node_implementation.control_protocol_property_changed, gate)); const auto& events_ws_port = nmos::fields::events_ws_port(node_model.settings); auto& events_ws_api = node_server.ws_handlers[{ {}, nmos::fields::events_ws_port(node_model.settings) }]; diff --git a/Development/nmos/node_server.h b/Development/nmos/node_server.h index 25a15d4b7..2dc96e4da 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -27,7 +27,7 @@ namespace nmos // underlying implementation into the server instance for the NMOS Node struct node_implementation { - node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed) + node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::get_properties_by_path_handler get_properties_by_path, nmos::validate_set_properties_by_path_handler validate_set_properties_by_path, nmos::set_properties_by_path_handler set_properties_by_path) : load_server_certificates(std::move(load_server_certificates)) , load_dh_param(std::move(load_dh_param)) , load_ca_certificates(std::move(load_ca_certificates)) @@ -50,6 +50,9 @@ namespace nmos , get_control_protocol_datatype_descriptor(std::move(get_control_protocol_datatype_descriptor)) , get_control_protocol_method_descriptor(std::move(get_control_protocol_method_descriptor)) , control_protocol_property_changed(std::move(control_protocol_property_changed)) + , get_properties_by_path(std::move(get_properties_by_path)) + , validate_set_properties_by_path(std::move(validate_set_properties_by_path)) + , set_properties_by_path(std::move(set_properties_by_path)) {} // use the default constructor and chaining member functions for fluent initialization @@ -82,6 +85,9 @@ namespace nmos node_implementation& on_get_control_datatype_descriptor(nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { this->get_control_protocol_datatype_descriptor = std::move(get_control_protocol_datatype_descriptor); return *this; } node_implementation& on_get_control_protocol_method_descriptor(nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor) { this->get_control_protocol_method_descriptor = std::move(get_control_protocol_method_descriptor); return *this; } node_implementation& on_control_protocol_property_changed(nmos::control_protocol_property_changed_handler control_protocol_property_changed) { this->control_protocol_property_changed = std::move(control_protocol_property_changed); return *this; } + node_implementation& on_get_properties_by_path(nmos::get_properties_by_path_handler get_properties_by_path) { this->get_properties_by_path = std::move(get_properties_by_path); return *this; } + node_implementation& on_validate_set_properties_by_path(nmos::validate_set_properties_by_path_handler validate_set_properties_by_path) { this->validate_set_properties_by_path = std::move(validate_set_properties_by_path); return *this; } + node_implementation& on_set_properties_by_path(nmos::set_properties_by_path_handler set_properties_by_path) { this->set_properties_by_path = std::move(set_properties_by_path); return *this; } // deprecated, use on_validate_connection_resource_patch node_implementation& on_validate_merged(nmos::details::connection_resource_patch_validator validate_merged) { return on_validate_connection_resource_patch(std::move(validate_merged)); } @@ -124,6 +130,11 @@ namespace nmos nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor; nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor; nmos::control_protocol_property_changed_handler control_protocol_property_changed; + + // Device Configuration method handlers + nmos::get_properties_by_path_handler get_properties_by_path; + nmos::validate_set_properties_by_path_handler validate_set_properties_by_path; + nmos::set_properties_by_path_handler set_properties_by_path; }; // Construct a server instance for an NMOS Node, implementing the IS-04 Node API, IS-05 Connection API, IS-07 Events API diff --git a/Development/nmos/test/control_protocol_test.cpp b/Development/nmos/test/control_protocol_test.cpp index 7150accd6..25364f652 100644 --- a/Development/nmos/test/control_protocol_test.cpp +++ b/Development/nmos/test/control_protocol_test.cpp @@ -676,7 +676,7 @@ BST_TEST_CASE(testFindProperty) const auto invalid_property_id = nmos::nc_property_id(1000, 1000); const auto invalid_class_id = nmos::nc_class_id({ 1000, 1000 }); - nmos::experimental::control_protocol_state control_protocol_state(nullptr); + nmos::experimental::control_protocol_state control_protocol_state; auto get_control_protocol_class_descriptor = nmos::make_get_control_protocol_class_descriptor_handler(control_protocol_state); { @@ -773,7 +773,7 @@ BST_TEST_CASE(testConstraints) const auto struct_datatype = nmos::details::make_nc_datatype_descriptor_struct(U("struct datatype"), U("structDatatype"), fields, value::null()); // no datatype constraints for struct datatype // setup datatypes in control_protocol_state - nmos::experimental::control_protocol_state control_protocol_state(nullptr); + nmos::experimental::control_protocol_state control_protocol_state; control_protocol_state.insert(nmos::experimental::datatype_descriptor{ no_constraints_int16_datatype }); control_protocol_state.insert(nmos::experimental::datatype_descriptor{ no_constraints_int32_datatype }); control_protocol_state.insert(nmos::experimental::datatype_descriptor{ no_constraints_int64_datatype }); diff --git a/Development/nmos/type.h b/Development/nmos/type.h index 8da37f685..eef5ca396 100644 --- a/Development/nmos/type.h +++ b/Development/nmos/type.h @@ -49,6 +49,7 @@ namespace nmos const type nc_receiver_monitor{ U("nc_receiver_monitor") }; const type nc_receiver_monitor_protected{ U("nc_receiver_monitor_protected") }; const type nc_ident_beacon{ U("nc_ident_beacon") }; + const type nc_bulk_properties_manager{ U("nc_bulk_properties_manager") }; } } From 3deaccbf1b6916235a40109436d83507f5309f83 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 20 Jun 2024 09:54:00 +0100 Subject: [PATCH 118/250] Use the correct events for NcBulkPropertiesManager class descriptor --- Development/nmos/control_protocol_state.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index e4258ea08..01c4e6452 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -400,7 +400,7 @@ namespace nmos { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(validate_set_properties_by_path_handler) }, { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(set_properties_by_path_handler) } }), - to_vector(make_nc_receiver_monitor_protected_events())) } + to_vector(make_nc_bulk_properties_manager_events())) } }; // setup the standard datatypes From bd9b59acbcf4036898b22bb8cbb9ee1a4c112a1f Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 20 Jun 2024 09:59:48 +0100 Subject: [PATCH 119/250] typo --- Development/nmos/control_protocol_state.cpp | 8 ++++---- Development/nmos/control_protocol_state.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 01c4e6452..5cb59feb0 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -256,7 +256,7 @@ namespace nmos } } - control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, get_properties_by_path_handler get_properties_by_path_handler, validate_set_properties_by_path_handler validate_set_properties_by_path_handler, set_properties_by_path_handler set_properties_by_path_handler) + control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, get_properties_by_path_handler get_properties_by_path, validate_set_properties_by_path_handler validate_set_properties_by_path, set_properties_by_path_handler set_properties_by_path) { using web::json::value; @@ -396,9 +396,9 @@ namespace nmos to_vector(make_nc_bulk_properties_manager_properties()), to_methods_vector(make_nc_bulk_properties_manager_methods(), { - { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(get_properties_by_path_handler) }, - { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(validate_set_properties_by_path_handler) }, - { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(set_properties_by_path_handler) } + { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(get_properties_by_path) }, + { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(validate_set_properties_by_path) }, + { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(set_properties_by_path) } }), to_vector(make_nc_bulk_properties_manager_events())) } }; diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index bb149bd1d..ee0904e8b 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -58,7 +58,7 @@ namespace nmos nmos::read_lock read_lock() const { return nmos::read_lock{ mutex }; } nmos::write_lock write_lock() const { return nmos::write_lock{ mutex }; } - control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, get_properties_by_path_handler get_properties_by_path_handler = nullptr, validate_set_properties_by_path_handler validate_set_properties_by_path_handler = nullptr, set_properties_by_path_handler set_properties_by_path_handler = nullptr); + control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, get_properties_by_path_handler get_properties_by_path = nullptr, validate_set_properties_by_path_handler validate_set_properties_by_path = nullptr, set_properties_by_path_handler set_properties_by_path = nullptr); // insert control class descriptor, false if class descriptor already inserted bool insert(const experimental::control_class_descriptor& control_class_descriptor); // erase control class of the given class id, false if the required class not found From 780c11a521925cb0a5896f99b6603c37703e10c5 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 20 Jun 2024 10:06:51 +0100 Subject: [PATCH 120/250] Remove unused code --- Development/nmos/control_protocol_state.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 5cb59feb0..6f2777a6c 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -258,8 +258,6 @@ namespace nmos control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, get_properties_by_path_handler get_properties_by_path, validate_set_properties_by_path_handler validate_set_properties_by_path, set_properties_by_path_handler set_properties_by_path) { - using web::json::value; - auto to_vector = [](const web::json::value& data) { if (!data.is_null()) From 226dc59e83a40d78f9e4c2b5cc20832421ad0829 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe <64410119+jonathan-r-thorpe@users.noreply.github.com> Date: Sun, 14 Jul 2024 12:13:17 +0100 Subject: [PATCH 121/250] IS-14 Refactor bulkProperties methods (#8) * Add Bulk Properties Manager to Device Model * Refactor bulkProperties GET method * Refactor bulkProperties PATCH and PUT methods * Updated according to latest specification --------- Co-authored-by: Simon Lo --- .../nmos-cpp-node/node_implementation.cpp | 8 +- Development/nmos/configuration_api.cpp | 197 +++++++++++++----- Development/nmos/configuration_api.h | 2 +- Development/nmos/control_protocol_handlers.h | 6 +- .../nmos/control_protocol_resource.cpp | 32 ++- Development/nmos/control_protocol_resource.h | 4 + Development/nmos/control_protocol_state.cpp | 29 +-- Development/nmos/control_protocol_typedefs.h | 30 +++ Development/nmos/json_fields.h | 3 +- Development/nmos/node_server.cpp | 2 +- 10 files changed, 236 insertions(+), 77 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 33a6251df..20d7767fd 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1287,6 +1287,8 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr nmos::push_back(root_block, class_manager); // add device-manager to root-block nmos::push_back(root_block, device_manager); + // add bulk-properties-manager to root-block + nmos::push_back(root_block, bulk_properties_manager); // insert control protocol resources to model insert_root_after(delay_millis, root_block, gate); @@ -1720,7 +1722,7 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control // 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) { - return [&resources, &gate](const nmos::resource& resource, bool recurse) + return [&resources, &gate](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, bool recurse) { slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do get_properties_by_path"; @@ -1732,7 +1734,7 @@ nmos::get_properties_by_path_handler make_node_implementation_get_properties_by_ // 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) { - return [&resources, &gate](const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse) + return [&resources, &gate](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) { slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do validate_set_properties_by_path"; @@ -1744,7 +1746,7 @@ nmos::validate_set_properties_by_path_handler make_node_implementation_validate_ // 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](const nmos::resource& resource, const web::json::value& data_set, bool recurse, bool allow_incomplete) + return [&resources, &gate](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) { slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do set_properties_by_path"; diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 3875bab33..0bd66255b 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -18,9 +18,9 @@ namespace nmos { - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_properties_by_path_handler get_properties_by_path, validate_set_properties_by_path_handler validate_set_properties_by_path, set_properties_by_path_handler set_properties_by_path, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_properties_by_path_handler get_properties_by_path, validate_set_properties_by_path_handler validate_set_properties_by_path, set_properties_by_path_handler set_properties_by_path, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -51,7 +51,7 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, get_properties_by_path, validate_set_properties_by_path, set_properties_by_path, property_changed, gate)); + configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, property_changed, gate)); return configuration_api; } @@ -218,16 +218,16 @@ namespace nmos { web::json::value arguments = web::json::value_from_query(query); - if (arguments.has_boolean_field(fields::nc::recurse)) + if (arguments.has_field(fields::nc::recurse)) { - return fields::nc::recurse(arguments); + return U("false") != arguments.at(fields::nc::recurse).as_string(); } return true; } } - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_properties_by_path_handler get_properties_by_path, validate_set_properties_by_path_handler validate_set_properties_by_path, set_properties_by_path_handler set_properties_by_path, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -647,122 +647,213 @@ namespace nmos }); }); - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::GET, [&model, get_properties_by_path, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::GET, [&model, get_control_protocol_method_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); auto lock = model.read_lock(); - auto& resources = model.control_protocol_resources; - const auto& resource = details::find_resource(resources, role_path); - if (resources.end() != resource) + const auto& bulk_properties_manager = details::find_resource(resources, nmos::bulk_properties_manager_role); + + if (resources.end() != resource && resources.end() != bulk_properties_manager) { - bool recurse = details::parse_recurse_query_parameter(req.request_uri().query()); + auto method = get_control_protocol_method_descriptor(nc_bulk_properties_manager_class_id, nc_bulk_properties_manager_get_properties_by_path_method_id); + auto& nc_method_descriptor = method.first; + auto& control_method_handler = method.second; + web::http::status_code code{ status_codes::BadRequest }; + value method_result; - auto result = details::make_nc_method_result_error({ nc_method_status::method_not_implemented }, U("get_properties_by_path not provided")); - if (get_properties_by_path) + if (control_method_handler) { - result = get_properties_by_path(*resource, recurse); + try + { + bool recurse = details::parse_recurse_query_parameter(req.request_uri().query()); + + method_result = control_method_handler(resources, *resource, value_of({ { nmos::fields::nc::recurse, recurse } }), nmos::fields::nc::is_deprecated(nc_method_descriptor), gate_); + + auto status = nmos::fields::nc::status(method_result); + if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } + else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } + else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } + else { code = status_codes::InternalError; } + } + catch (const nmos::control_protocol_exception& e) + { + // invalid arguments + utility::stringstream_t ss; + ss << U("parameter error: ") << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + + code = status_codes::BadRequest; + } } + else + { + // unknown methodId + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("get_properties_by_path unsupported by bulk properties manager.")); - auto status = nmos::fields::nc::status(result); - auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; - set_reply(res, code, result); + code = status_codes::NotFound; + } + set_reply(res, code, method_result); } else { - // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + if (resources.end() == bulk_properties_manager) + { + // no bulk properties manager + set_error_reply(res, status_codes::NotFound, U("Bulk Properties Manager not found at ") + nmos::bulk_properties_manager_role); + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } } return pplx::task_from_result(true); }); - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, validate_set_properties_by_path, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_method_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); + const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); auto lock = model.read_lock(); - const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); auto& resources = model.control_protocol_resources; - const auto& resource = details::find_resource(resources, role_path); - if (resources.end() != resource) + const auto& bulk_properties_manager = details::find_resource(resources, nmos::bulk_properties_manager_role); + if (resources.end() != resource && resources.end() != bulk_properties_manager) { - return details::extract_json(req, gate_).then([res, resources, resource, validate_set_properties_by_path, version, &gate_](value body) mutable + return details::extract_json(req, gate_).then([res, resources, resource, get_control_protocol_method_descriptor, version, &gate_](value body) mutable { // Validate JSON syntax according to the schema details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_validate_request_schema_uri(version)); - bool recurse = nmos::fields::nc::recurse(body); - const auto& data_set = nmos::fields::nc::data_set(body); - if (!data_set.is_null()) + auto method = get_control_protocol_method_descriptor(nc_bulk_properties_manager_class_id, nc_bulk_properties_manager_validate_set_properties_by_path_method_id); + auto& nc_method_descriptor = method.first; + auto& control_method_handler = method.second; + web::http::status_code code{ status_codes::BadRequest }; + value method_result; + + if (control_method_handler) { - auto result = details::make_nc_method_result_error({ nc_method_status::method_not_implemented }, U("validate_set_properties_by_path not provided")); - if (validate_set_properties_by_path) + try { - result = validate_set_properties_by_path(*resource, data_set, recurse); + method_result = control_method_handler(resources, *resource, nmos::fields::nc::arguments(body), nmos::fields::nc::is_deprecated(nc_method_descriptor), gate_); + + auto status = nmos::fields::nc::status(method_result); + if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } + else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } + else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } + else { code = status_codes::InternalError; } } + catch (const nmos::control_protocol_exception& e) + { + // invalid arguments + utility::stringstream_t ss; + ss << U("parameter error: ") << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); - auto status = nmos::fields::nc::status(result); - auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; - set_reply(res, code, result); + code = status_codes::BadRequest; + } } else { - set_reply(res, status_codes::BadRequest, nmos::details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("Null dataSet parameter"))); + // unknown methodId + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("validate_set_properties_by_path unsupported by bulk properties manager.")); + + code = status_codes::NotFound; } + set_reply(res, code, method_result); + return true; }); } else { - // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + if (resources.end() == bulk_properties_manager) + { + // no bulk properties manager + set_error_reply(res, status_codes::NotFound, U("Bulk Properties Manager not found at ") + nmos::bulk_properties_manager_role); + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } } return pplx::task_from_result(true); }); - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, set_properties_by_path, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_method_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); + const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); auto lock = model.read_lock(); - const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); auto& resources = model.control_protocol_resources; - const auto& resource = details::find_resource(resources, role_path); - if (resources.end() != resource) + const auto& bulk_properties_manager = details::find_resource(resources, nmos::bulk_properties_manager_role); + if (resources.end() != resource && resources.end() != bulk_properties_manager) { - return details::extract_json(req, gate_).then([res, resources, resource, set_properties_by_path, version, &gate_](value body) mutable + return details::extract_json(req, gate_).then([res, resources, resource, get_control_protocol_method_descriptor, version, &gate_](value body) mutable { // Validate JSON syntax according to the schema details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_set_request_schema_uri(version)); - const auto& arguments = nmos::fields::nc::arguments(body); - bool recurse = nmos::fields::nc::recurse(arguments); - bool allow_incomplete = nmos::fields::nc::allow_incomplete(arguments); - const auto& data_set = nmos::fields::nc::data_set(arguments); + auto method = get_control_protocol_method_descriptor(nc_bulk_properties_manager_class_id, nc_bulk_properties_manager_set_properties_by_path_method_id); + auto& nc_method_descriptor = method.first; + auto& control_method_handler = method.second; + web::http::status_code code{ status_codes::BadRequest }; + value method_result; - auto result = details::make_nc_method_result_error({ nc_method_status::method_not_implemented }, U("set_properties_by_path not provided")); - if (set_properties_by_path) + if (control_method_handler) { - result = set_properties_by_path(*resource, data_set, recurse, allow_incomplete); + try + { + method_result = control_method_handler(resources, *resource, nmos::fields::nc::arguments(body), nmos::fields::nc::is_deprecated(nc_method_descriptor), gate_); + auto status = nmos::fields::nc::status(method_result); + if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } + else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } + else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } + else { code = status_codes::InternalError; } + } + catch (const nmos::control_protocol_exception& e) + { + // invalid arguments + utility::stringstream_t ss; + ss << U("parameter error: ") << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + + code = status_codes::BadRequest; + } } + else + { + // unknown methodId + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("set_properties_by_path unsupported by bulk properties manager.")); - auto status = nmos::fields::nc::status(result); - auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; - set_reply(res, code, result); + code = status_codes::NotFound; + } + set_reply(res, code, method_result); return true; }); } else { - // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + if (resources.end() == bulk_properties_manager) + { + // no bulk properties manager + set_error_reply(res, status_codes::NotFound, U("Bulk Properties Manager not found at ") + nmos::bulk_properties_manager_role); + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } } return pplx::task_from_result(true); diff --git a/Development/nmos/configuration_api.h b/Development/nmos/configuration_api.h index 6e4b8f081..9b1a73297 100644 --- a/Development/nmos/configuration_api.h +++ b/Development/nmos/configuration_api.h @@ -15,7 +15,7 @@ namespace nmos { struct node_model; - web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_properties_by_path_handler get_properties_by_path, validate_set_properties_by_path_handler validate_set_properties_by_path, set_properties_by_path_handler set_properties_by_path, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); } #endif diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index e7d5fda03..d57972b28 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -38,9 +38,9 @@ namespace nmos // Device Configuration handlers // these callbacks should not throw exceptions - typedef std::function get_properties_by_path_handler; - typedef std::function validate_set_properties_by_path_handler; - typedef std::function set_properties_by_path_handler; + typedef std::function get_properties_by_path_handler; + typedef std::function validate_set_properties_by_path_handler; + typedef std::function set_properties_by_path_handler; namespace experimental { diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 9c16334cb..ccbecdb7b 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -1262,6 +1262,7 @@ namespace nmos web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkValuesHolder"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If populated (not an empty collection) will include the properties matching any of the specified traits in the restore validation. When not populated only properties without traits are validated for restore"), nmos::fields::nc::included_property_traits, U("NcPropertyTrait"), false, true, value::null())); web::json::push_back(methods, details::make_nc_method_descriptor(U("Validate bulk properties for setting by given paths"), nc_bulk_properties_manager_validate_set_properties_by_path_method_id, U("ValidateSetPropertiesByPath"), U("NcMethodResultObjectPropertiesSetValidation"), parameters, false)); } { @@ -1269,7 +1270,7 @@ namespace nmos web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkValuesHolder"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will allow the device to restore only the role paths which pass validation(perform an incomplete restore)"), nmos::fields::nc::allow_incomplete, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If populated (not an empty collection) will include the properties matching any of the specified traits in the restore validation. When not populated only properties without traits are validated for restore"), nmos::fields::nc::included_property_traits, U("NcPropertyTrait"), false, true, value::null())); web::json::push_back(methods, details::make_nc_method_descriptor(U("Set bulk properties for setting by given paths"), nc_bulk_properties_manager_set_properties_by_path_method_id, U("SetPropertiesByPath"), U("NcMethodResultObjectPropertiesSetValidation"), parameters, false)); } @@ -2116,6 +2117,18 @@ namespace nmos // Device Configuration datatypes // TODO: add link + web::json::value make_nc_property_trait_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Property is instance specific"), U("InstanceSpecific"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Property is ephemeral"), U("Ephemeral"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Property is immutable"), U("Immutable"), 3)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Property value is generated by the device"), U("DeviceGenerated"), 4)); + return details::make_nc_datatype_descriptor_enum(U("Property trait enumeration"), U("NcPropertyTrait"), items, value::null()); + } + // TODO: add link web::json::value make_nc_property_value_holder_datatype() { using web::json::value; @@ -2125,6 +2138,7 @@ namespace nmos web::json::push_back(fields, details::make_nc_field_descriptor(U("Property name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Property type name. If null it means the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Is the property ReadOnly?"), nmos::fields::nc::is_read_only, U("NcBoolean"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Describes the property traits as a collection of unique items"), nmos::fields::nc::traits, U("NcPropertyTrait"), false, true, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); return details::make_nc_datatype_descriptor_struct(U("Property value holder descriptor"), U("NcPropertyValueHolder"), fields, value::null()); @@ -2152,13 +2166,27 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(U("Bulk values holder descriptor"), U("NcBulkValuesHolder"), fields, value::null()); } // TODO: add link + web::json::value make_nc_restore_validation_status_datatype() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore was successful"), U("Ok"), 200)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Excluded from restore due to data provided in the request"), U("Excluded"), 204)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed because relevant backup data set provided is invalid"), U("InvalidData"), 400)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed because the role path is not found in the device model or the device cannot create the role path from the data set"), U("NotFound"), 404)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed because of missing dependency information in the relevant backup data set"), U("MissingDependency"), 424)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed due to an internal device error preventing the restore from happening"), U("DeviceError"), 500)); + return details::make_nc_datatype_descriptor_enum(U("Restore validation status enumeration"), U("NcRestoreValidationStatus"), items, value::null()); + } + // TODO: add link web::json::value make_nc_object_properties_set_validation_datatype() { using web::json::value; auto fields = value::array(); web::json::push_back(fields, details::make_nc_field_descriptor(U("Object role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation status"), nmos::fields::nc::status, U("NcMethodStatus"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation status"), nmos::fields::nc::status, U("NcRestoreValidationStatus"), false, false, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation status message"), nmos::fields::nc::status_message, U("NcString"), true, false, value::null())); return details::make_nc_datatype_descriptor_struct(U("Object properties set validation descriptor"), U("NcObjectPropertiesSetValidation"), fields, value::null()); diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 37948dc4e..8336b62f6 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -441,12 +441,16 @@ namespace nmos // Device configuration feature set datatypes // TODO: add link // + web::json::value make_nc_property_trait_datatype(); + // web::json::value make_nc_property_value_holder_datatype(); // web::json::value make_nc_object_properties_holder_datatype(); // web::json::value make_nc_bulk_values_holder_datatype(); // + web::json::value make_nc_restore_validation_status_datatype(); + // web::json::value make_nc_object_properties_set_validation_datatype(); // web::json::value make_nc_method_result_bulk_values_holder_datatype(); diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 6f2777a6c..fd6eb2796 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -179,9 +179,9 @@ namespace nmos return get_datatype(resources, resource, arguments, is_deprecated, get_control_protocol_datatype_descriptor, gate); }; } - nmos::experimental::control_protocol_method_handler make_nc_get_properties_by_path_handler(get_properties_by_path_handler get_properties_by_path) + nmos::experimental::control_protocol_method_handler make_nc_get_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_properties_by_path_handler get_properties_by_path) { - return [get_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); @@ -190,7 +190,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); if (get_properties_by_path) { - result = get_properties_by_path(resource, recurse); + result = get_properties_by_path(get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, recurse); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -201,11 +201,12 @@ namespace nmos return result; }; } - nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(validate_set_properties_by_path_handler validate_set_properties_by_path) + nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, validate_set_properties_by_path_handler validate_set_properties_by_path) { - return [validate_set_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_set_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); + const auto& included_property_traits = nmos::fields::nc::included_property_traits(arguments); const auto& data_set = nmos::fields::nc::data_set(arguments); if (data_set.is_null()) @@ -216,7 +217,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); if (validate_set_properties_by_path) { - result = validate_set_properties_by_path(resource, data_set, recurse); + result = validate_set_properties_by_path(get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, data_set, recurse, included_property_traits); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -227,12 +228,12 @@ namespace nmos return result; }; } - nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(set_properties_by_path_handler set_properties_by_path) + nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, set_properties_by_path_handler set_properties_by_path) { - return [set_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, set_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); - bool allow_incomplete = nmos::fields::nc::allow_incomplete(arguments); + const auto& included_property_traits = nmos::fields::nc::included_property_traits(arguments); const auto& data_set = nmos::fields::nc::data_set(arguments); if (data_set.is_null()) @@ -243,7 +244,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); if (set_properties_by_path) { - result = set_properties_by_path(resource, data_set, recurse, allow_incomplete); + result = set_properties_by_path(get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, data_set, recurse, included_property_traits); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -394,9 +395,9 @@ namespace nmos to_vector(make_nc_bulk_properties_manager_properties()), to_methods_vector(make_nc_bulk_properties_manager_methods(), { - { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(get_properties_by_path) }, - { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(validate_set_properties_by_path) }, - { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(set_properties_by_path) } + { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), get_properties_by_path)}, + { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), validate_set_properties_by_path) }, + { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), set_properties_by_path) } }), to_vector(make_nc_bulk_properties_manager_events())) } }; @@ -480,9 +481,11 @@ namespace nmos { U("NcPayloadStatus"), {make_nc_payload_status_datatype()} }, // Device configuration feature set // TODO: add link + { U("NcPropertyTrait"), {make_nc_property_trait_datatype()} }, { U("NcPropertyValueHolder"), {make_nc_property_value_holder_datatype()}}, { U("NcObjectPropertiesHolder"), {make_nc_object_properties_holder_datatype()}}, { U("NcBulkValuesHolder"), {make_nc_bulk_values_holder_datatype()}}, + { U("NcRestoreValidationStatus"), {make_nc_restore_validation_status_datatype()}}, { U("NcObjectPropertiesSetValidation"), {make_nc_object_properties_set_validation_datatype()}}, { U("NcMethodResultBulkValuesHolder"), {make_nc_method_result_bulk_values_holder_datatype()}}, { U("NcMethodResultObjectPropertiesSetValidation"), {make_nc_method_result_object_properties_set_validation_datatype()}} diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index e65b03c47..9eaacc12b 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -124,6 +124,33 @@ namespace nmos }; } + // Device Configuration + // NcPropertyTrait + namespace nc_property_trait + { + enum trait + { + instance_specific = 1, // Property is instance specific + ephemeral = 2, // Property is ephemeral + immutable = 3, // Property is immutable + device_generated = 4 // Property value is generated by the device + }; + } + + // NcRestoreValidationStatus + namespace nc_restore_validation_status + { + enum staus + { + ok = 200, // Restore was successful + excluded = 204, // Excluded from restore due to data provided in the request + invalid_data = 400, // Restore failed because relevant backup data set provided is invalid + not_found = 404, // Restore failed because the role path is not found in the device model or the device cannot create the role path from the data set + missing_dependency = 424, // Restore failed because of missing dependency information in the relevant backup data set + device_error = 500 // Restore failed due to an internal device error preventing the restore from happening + }; + } + // NcElementId // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncelementid struct nc_element_id @@ -391,6 +418,9 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Blocks.html const nc_oid root_block_oid{ 1 }; const utility::string_t root_block_role{ U("root") }; + + // Device Configuration + const utility::string_t bulk_properties_manager_role{ root_block_role + U(".BulkPropertiesManager") }; } #endif diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index dc000c836..13d45afa2 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -340,7 +340,8 @@ namespace nmos const web::json::field_as_string validation_fingerprint{ U("validationFingerprint") }; const web::json::field_as_string status_message{ U("statusMessage") }; const web::json::field_as_value data_set{ U("dataSet") }; // NcBulkValuesHolder - const web::json::field_as_bool allow_incomplete{ U("allowIncomplete") }; + const web::json::field_as_value traits{ U("traits") }; + const web::json::field_as_array included_property_traits{ U("includedPropertyTraits") }; } // NMOS Parameter Registers diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index c665a828b..1e9a97da2 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -77,7 +77,7 @@ namespace nmos // Configure the Configuration API - node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.get_properties_by_path, node_implementation.validate_set_properties_by_path, node_implementation.set_properties_by_path, node_implementation.control_protocol_property_changed, gate)); + node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.control_protocol_property_changed, gate)); const auto& events_ws_port = nmos::fields::events_ws_port(node_model.settings); auto& events_ws_api = node_server.ws_handlers[{ {}, nmos::fields::events_ws_port(node_model.settings) }]; From 783033032bd11e590ea2e51e92d9ed5abcae963a Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Tue, 6 Aug 2024 16:32:52 +0100 Subject: [PATCH 122/250] Remove redundant parameters. --- Development/nmos/control_protocol_methods.cpp | 6 +++--- Development/nmos/control_protocol_methods.h | 6 +++--- Development/nmos/control_protocol_state.cpp | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index d9d781294..72cf372e5 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -12,7 +12,7 @@ namespace nmos { // NcObject methods implementation // Get property value - web::json::value get(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + web::json::value get(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -95,7 +95,7 @@ namespace nmos } // Get sequence item - web::json::value get_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + web::json::value get_sequence_item(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -327,7 +327,7 @@ namespace nmos } // Get sequence length - web::json::value get_sequence_length(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + web::json::value get_sequence_length(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... diff --git a/Development/nmos/control_protocol_methods.h b/Development/nmos/control_protocol_methods.h index 957f93627..38858b9b8 100644 --- a/Development/nmos/control_protocol_methods.h +++ b/Development/nmos/control_protocol_methods.h @@ -13,11 +13,11 @@ namespace nmos { // NcObject methods implementation // Get property value - web::json::value get(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + web::json::value get(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); // Set property value web::json::value set(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); // Get sequence item - web::json::value get_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + web::json::value get_sequence_item(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); // Set sequence item web::json::value set_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); // Add item to sequence @@ -25,7 +25,7 @@ namespace nmos // Delete sequence item web::json::value remove_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); // Get sequence length - web::json::value get_sequence_length(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + web::json::value get_sequence_length(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); // NcBlock methods implementation // Get descriptors of members of the block diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index dd4b1911a..2956bfc7d 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -91,7 +91,7 @@ namespace nmos { return [get_control_protocol_class_descriptor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return get(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); + return get(resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_set_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed) @@ -105,7 +105,7 @@ namespace nmos { return [get_control_protocol_class_descriptor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return get_sequence_item(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); + return get_sequence_item(resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_set_sequence_item_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed) @@ -133,7 +133,7 @@ namespace nmos { return [get_control_protocol_class_descriptor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return get_sequence_length(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); + return get_sequence_length(resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_get_member_descriptors_handler() From b14f570c4c795f35213bee35d0cb3d2dbc44c607 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe <64410119+jonathan-r-thorpe@users.noreply.github.com> Date: Fri, 16 Aug 2024 16:00:51 +0100 Subject: [PATCH 123/250] Apply suggestions from code review Co-authored-by: Simon Lo --- Development/nmos/control_protocol_state.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 2956bfc7d..86441205b 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -89,7 +89,7 @@ namespace nmos { nmos::experimental::control_protocol_method_handler make_nc_get_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { - return [get_control_protocol_class_descriptor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor](nmos::resources&, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { return get(resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); }; @@ -103,7 +103,7 @@ namespace nmos } nmos::experimental::control_protocol_method_handler make_nc_get_sequence_item_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { - return [get_control_protocol_class_descriptor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor](nmos::resources&, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { return get_sequence_item(resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); }; @@ -131,7 +131,7 @@ namespace nmos } nmos::experimental::control_protocol_method_handler make_nc_get_sequence_length_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { - return [get_control_protocol_class_descriptor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor](nmos::resources&, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { return get_sequence_length(resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); }; From fa2eea2d935a03be51363d37d3e3d000ecc74d34 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 4 Sep 2024 16:06:39 +0100 Subject: [PATCH 124/250] Changed ncp_nmos_resource_type to ncp_touchpoint_resource_type --- .../nmos-cpp-node/node_implementation.cpp | 2 +- .../nmos/control_protocol_nmos_resource_type.h | 16 ++++++++-------- Development/nmos/control_protocol_typedefs.h | 2 +- Development/nmos/control_protocol_utils.cpp | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 20d7767fd..c78104029 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1266,7 +1266,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr utility::stringstream_t role; role << U("monitor-") << ++count; const auto& receiver = nmos::find_resource(model.node_resources, receiver_id); - const auto receiver_monitor = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::details::make_nc_touchpoint_nmos({nmos::ncp_nmos_resource_types::receiver, receiver_id}) } })); + const auto receiver_monitor = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, receiver_id}) } })); // add receiver-monitor to root-block nmos::push_back(root_block, receiver_monitor); diff --git a/Development/nmos/control_protocol_nmos_resource_type.h b/Development/nmos/control_protocol_nmos_resource_type.h index 436b72257..b6c4aa16c 100644 --- a/Development/nmos/control_protocol_nmos_resource_type.h +++ b/Development/nmos/control_protocol_nmos_resource_type.h @@ -8,15 +8,15 @@ namespace nmos { // resourceType // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresourcenmos - DEFINE_STRING_ENUM(ncp_nmos_resource_type) - namespace ncp_nmos_resource_types + DEFINE_STRING_ENUM(ncp_touchpoint_resource_type) + namespace ncp_touchpoint_resource_types { - const ncp_nmos_resource_type node{ U("node") }; - const ncp_nmos_resource_type device{ U("device") }; - const ncp_nmos_resource_type source{ U("source") }; - const ncp_nmos_resource_type flow{ U("flow") }; - const ncp_nmos_resource_type sender{ U("sender") }; - const ncp_nmos_resource_type receiver{ U("receiver") }; + const ncp_touchpoint_resource_type node{ U("node") }; + const ncp_touchpoint_resource_type device{ U("device") }; + const ncp_touchpoint_resource_type source{ U("source") }; + const ncp_touchpoint_resource_type flow{ U("flow") }; + const ncp_touchpoint_resource_type sender{ U("sender") }; + const ncp_touchpoint_resource_type receiver{ U("receiver") }; } } diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index 9eaacc12b..428036635 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -385,7 +385,7 @@ namespace nmos , id(id) {} - nc_touchpoint_resource_nmos(const ncp_nmos_resource_type& resource_type, nc_uuid id) + nc_touchpoint_resource_nmos(const ncp_touchpoint_resource_type& resource_type, nc_uuid id) : nc_touchpoint_resource(resource_type.name) , id(id) {} diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index dd9afc31c..df9ca7707 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -624,7 +624,7 @@ namespace nmos { auto& resource = nmos::fields::nc::resource(touchpoint); return (resource_id == nmos::fields::nc::id(resource).as_string() - && nmos::ncp_nmos_resource_types::receiver.name == nmos::fields::nc::resource_type(resource)); + && nmos::ncp_touchpoint_resource_types::receiver.name == nmos::fields::nc::resource_type(resource)); }); return (tps.end() != found_tp); } From 8210589008a05a6a79a58bdeb1f9e7ed212436e7 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 4 Sep 2024 16:17:56 +0100 Subject: [PATCH 125/250] Generalize find_control_protocol_resource --- Development/nmos/control_protocol_utils.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index df9ca7707..19d054ef8 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -623,8 +623,7 @@ namespace nmos auto found_tp = std::find_if(tps.begin(), tps.end(), [resource_id](const web::json::value& touchpoint) { auto& resource = nmos::fields::nc::resource(touchpoint); - return (resource_id == nmos::fields::nc::id(resource).as_string() - && nmos::ncp_touchpoint_resource_types::receiver.name == nmos::fields::nc::resource_type(resource)); + return (resource_id == nmos::fields::nc::id(resource).as_string()); }); return (tps.end() != found_tp); } From 87cfb856329826fbad6e6317f79eec4705d5a0fd Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 4 Sep 2024 16:27:37 +0100 Subject: [PATCH 126/250] Remove redundant parameters --- Development/nmos/control_protocol_methods.cpp | 4 ++-- Development/nmos/control_protocol_methods.h | 4 ++-- Development/nmos/control_protocol_state.cpp | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index 72cf372e5..9b43151ea 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -513,7 +513,7 @@ namespace nmos // NcClassManager methods implementation // Get a single class descriptor - web::json::value get_control_class(nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + web::json::value get_control_class(const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { using web::json::value; @@ -568,7 +568,7 @@ namespace nmos } // Get a single datatype descriptor - web::json::value get_datatype(nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, slog::base_gate& gate) + web::json::value get_datatype(const web::json::value& arguments, bool is_deprecated, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, slog::base_gate& gate) { // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... diff --git a/Development/nmos/control_protocol_methods.h b/Development/nmos/control_protocol_methods.h index 38858b9b8..b406b8860 100644 --- a/Development/nmos/control_protocol_methods.h +++ b/Development/nmos/control_protocol_methods.h @@ -39,9 +39,9 @@ namespace nmos // NcClassManager methods implementation // Get a single class descriptor - web::json::value get_control_class(nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + web::json::value get_control_class(const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); // Get a single datatype descriptor - web::json::value get_datatype(nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype, slog::base_gate& gate); + web::json::value get_datatype(const web::json::value& arguments, bool is_deprecated, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype, slog::base_gate& gate); } #endif diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index fde69a792..22887e349 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -167,16 +167,16 @@ namespace nmos } nmos::experimental::control_protocol_method_handler make_nc_get_control_class_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { - return [get_control_protocol_class_descriptor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor](nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return get_control_class(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); + return get_control_class(arguments, is_deprecated, get_control_protocol_class_descriptor, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_get_datatype_handler(get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { - return [get_control_protocol_datatype_descriptor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_datatype_descriptor](nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return get_datatype(resources, resource, arguments, is_deprecated, get_control_protocol_datatype_descriptor, gate); + return get_datatype(arguments, is_deprecated, get_control_protocol_datatype_descriptor, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_get_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_properties_by_path_handler get_properties_by_path) From 5f4ddd37e6c06e6aeb407fe828b2256e2dc7c356 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 27 Nov 2024 16:26:52 +0000 Subject: [PATCH 127/250] merge_patch doesn't work when argument value is null --- Development/nmos/configuration_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 0bd66255b..26fa3b717 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -627,8 +627,8 @@ namespace nmos { auto arguments = value_of({ { nmos::fields::nc::id, details::make_nc_property_id(details::parse_formatted_property_id(property_id))}, + { nmos::fields::nc::value, nmos::fields::nc::value(body)} }); - web::json::merge_patch(arguments, body, true); auto result = set(resources, *resource, arguments, false, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate); From 22cb52aac6e687f324b1abcd23ba400398f746ef Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Thu, 28 Nov 2024 13:46:42 +0000 Subject: [PATCH 128/250] Updated IS-14 datatypes, class and schemas --- .../nmos-cpp-node/node_implementation.cpp | 4 +- Development/nmos/control_protocol_handlers.h | 4 +- .../nmos/control_protocol_resource.cpp | 48 ++++++++++++++----- Development/nmos/control_protocol_resource.h | 6 ++- Development/nmos/control_protocol_state.cpp | 12 +++-- Development/nmos/json_fields.h | 7 ++- .../schemas/bulkProperties-set-request.json | 17 +++++-- .../bulkProperties-validate-request.json | 15 ++++-- .../v1.0.x/APIs/schemas/methods-base.json | 3 +- .../v1.0.x/APIs/schemas/properties-base.json | 3 +- .../is-14/v1.0.x/APIs/schemas/rolePath.json | 4 +- 11 files changed, 87 insertions(+), 36 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index c78104029..0b20086cf 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1734,7 +1734,7 @@ nmos::get_properties_by_path_handler make_node_implementation_get_properties_by_ // 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) { - return [&resources, &gate](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::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& restore_mode) { slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do validate_set_properties_by_path"; @@ -1746,7 +1746,7 @@ nmos::validate_set_properties_by_path_handler make_node_implementation_validate_ // 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::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::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::value& restore_mode) { slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do set_properties_by_path"; diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index d57972b28..94c2c2b30 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -39,8 +39,8 @@ namespace nmos // Device Configuration handlers // these callbacks should not throw exceptions typedef std::function get_properties_by_path_handler; - typedef std::function validate_set_properties_by_path_handler; - typedef std::function set_properties_by_path_handler; + typedef std::function validate_set_properties_by_path_handler; + typedef std::function set_properties_by_path_handler; namespace experimental { diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index ccbecdb7b..b79921579 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -1262,7 +1262,7 @@ namespace nmos web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkValuesHolder"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If populated (not an empty collection) will include the properties matching any of the specified traits in the restore validation. When not populated only properties without traits are validated for restore"), nmos::fields::nc::included_property_traits, U("NcPropertyTrait"), false, true, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Defines the restore mode to be applied"), nmos::fields::nc::restore_mode, U("NcRestoreMode"), false, false, value::null())); web::json::push_back(methods, details::make_nc_method_descriptor(U("Validate bulk properties for setting by given paths"), nc_bulk_properties_manager_validate_set_properties_by_path_method_id, U("ValidateSetPropertiesByPath"), U("NcMethodResultObjectPropertiesSetValidation"), parameters, false)); } { @@ -1270,7 +1270,7 @@ namespace nmos web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkValuesHolder"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If populated (not an empty collection) will include the properties matching any of the specified traits in the restore validation. When not populated only properties without traits are validated for restore"), nmos::fields::nc::included_property_traits, U("NcPropertyTrait"), false, true, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Defines the restore mode to be applied"), nmos::fields::nc::restore_mode, U("NcRestoreMode"), false, false, value::null())); web::json::push_back(methods, details::make_nc_method_descriptor(U("Set bulk properties for setting by given paths"), nc_bulk_properties_manager_set_properties_by_path_method_id, U("SetPropertiesByPath"), U("NcMethodResultObjectPropertiesSetValidation"), parameters, false)); } @@ -2117,16 +2117,15 @@ namespace nmos // Device Configuration datatypes // TODO: add link - web::json::value make_nc_property_trait_datatype() + web::json::value make_nc_restore_mode_datatype() { using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Property is instance specific"), U("InstanceSpecific"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Property is ephemeral"), U("Ephemeral"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Property is immutable"), U("Immutable"), 3)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Property value is generated by the device"), U("DeviceGenerated"), 4)); - return details::make_nc_datatype_descriptor_enum(U("Property trait enumeration"), U("NcPropertyTrait"), items, value::null()); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore mode is Modify"), U("Modify"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore mode is Rebuild"), U("Rebuild"), 1)); + + return details::make_nc_datatype_descriptor_enum(U("Restore mode enumeration"), U("NcRestoreMode"), items, value::null()); } // TODO: add link web::json::value make_nc_property_value_holder_datatype() @@ -2137,8 +2136,7 @@ namespace nmos web::json::push_back(fields, details::make_nc_field_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Property name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Property type name. If null it means the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Is the property ReadOnly?"), nmos::fields::nc::is_read_only, U("NcBoolean"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Describes the property traits as a collection of unique items"), nmos::fields::nc::traits, U("NcPropertyTrait"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Is the property ReadOnly?"), nmos::fields::nc::is_read_only, U("NcBoolean"), false, false, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); return details::make_nc_datatype_descriptor_struct(U("Property value holder descriptor"), U("NcPropertyValueHolder"), fields, value::null()); @@ -2151,6 +2149,7 @@ namespace nmos auto fields = value::array(); web::json::push_back(fields, details::make_nc_field_descriptor(U("Object role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Object properties values"), nmos::fields::nc::values, U("NcPropertyValueHolder"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Describes if the object is rebuildable"), nmos::fields::nc::is_rebuildable, U("NcBoolean"), false, false, value::null())); return details::make_nc_datatype_descriptor_struct(U("Object properties holder descriptor"), U("NcObjectPropertiesHolder"), fields, value::null()); } @@ -2172,14 +2171,36 @@ namespace nmos auto items = value::array(); web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore was successful"), U("Ok"), 200)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Excluded from restore due to data provided in the request"), U("Excluded"), 204)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed because relevant backup data set provided is invalid"), U("InvalidData"), 400)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed"), U("Failed"), 400)); web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed because the role path is not found in the device model or the device cannot create the role path from the data set"), U("NotFound"), 404)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed because of missing dependency information in the relevant backup data set"), U("MissingDependency"), 424)); web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed due to an internal device error preventing the restore from happening"), U("DeviceError"), 500)); return details::make_nc_datatype_descriptor_enum(U("Restore validation status enumeration"), U("NcRestoreValidationStatus"), items, value::null()); } // TODO: add link + web::json::value make_nc_property_restore_notice_type() + { + using web::json::value; + + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Warning property restore notice"), U("Warning"), 300)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Error property restore notice"), U("Error"), 400)); + + return details::make_nc_datatype_descriptor_enum(U("Property restore notice type enumeration"), U("NcPropertyRestoreNoticeType"), items, value::null()); + } + // TODO: add link + web::json::value make_nc_property_restore_notice() + { + using web::json::value; + + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property name"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property restore notice type"), nmos::fields::nc::notice_type, U("NcPropertyRestoreNoticeType"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property restore notice message"), nmos::fields::nc::notice_message, U("NcString"), false, false, value::null())); + + return details::make_nc_datatype_descriptor_struct(U("Bulk values holder descriptor"), U("NcPropertyRestoreNotice"), fields, value::null()); + } + // TODO: add link web::json::value make_nc_object_properties_set_validation_datatype() { using web::json::value; @@ -2187,6 +2208,7 @@ namespace nmos auto fields = value::array(); web::json::push_back(fields, details::make_nc_field_descriptor(U("Object role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation status"), nmos::fields::nc::status, U("NcRestoreValidationStatus"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation property notices"), nmos::fields::nc::notices, U("NcPropertyRestoreNotice"), false, true, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation status message"), nmos::fields::nc::status_message, U("NcString"), true, false, value::null())); return details::make_nc_datatype_descriptor_struct(U("Object properties set validation descriptor"), U("NcObjectPropertiesSetValidation"), fields, value::null()); diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 8336b62f6..b50bd4035 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -441,7 +441,7 @@ namespace nmos // Device configuration feature set datatypes // TODO: add link // - web::json::value make_nc_property_trait_datatype(); + web::json::value make_nc_restore_mode_datatype(); // web::json::value make_nc_property_value_holder_datatype(); // @@ -451,6 +451,10 @@ namespace nmos // web::json::value make_nc_restore_validation_status_datatype(); // + web::json::value make_nc_property_restore_notice_type(); + // + web::json::value make_nc_property_restore_notice(); + // web::json::value make_nc_object_properties_set_validation_datatype(); // web::json::value make_nc_method_result_bulk_values_holder_datatype(); diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 22887e349..56542dd89 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -206,7 +206,7 @@ namespace nmos return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_set_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); - const auto& included_property_traits = nmos::fields::nc::included_property_traits(arguments); + const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); const auto& data_set = nmos::fields::nc::data_set(arguments); if (data_set.is_null()) @@ -217,7 +217,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); if (validate_set_properties_by_path) { - result = validate_set_properties_by_path(get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, data_set, recurse, included_property_traits); + result = validate_set_properties_by_path(get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, data_set, recurse, restore_mode); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -233,7 +233,7 @@ namespace nmos return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, set_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); - const auto& included_property_traits = nmos::fields::nc::included_property_traits(arguments); + const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); const auto& data_set = nmos::fields::nc::data_set(arguments); if (data_set.is_null()) @@ -244,7 +244,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); if (set_properties_by_path) { - result = set_properties_by_path(get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, data_set, recurse, included_property_traits); + result = set_properties_by_path(get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, data_set, recurse, restore_mode); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -481,11 +481,13 @@ namespace nmos { U("NcPayloadStatus"), {make_nc_payload_status_datatype()} }, // Device configuration feature set // TODO: add link - { U("NcPropertyTrait"), {make_nc_property_trait_datatype()} }, + { U("NcRestoreMode"), {make_nc_restore_mode_datatype()} }, { U("NcPropertyValueHolder"), {make_nc_property_value_holder_datatype()}}, { U("NcObjectPropertiesHolder"), {make_nc_object_properties_holder_datatype()}}, { U("NcBulkValuesHolder"), {make_nc_bulk_values_holder_datatype()}}, { U("NcRestoreValidationStatus"), {make_nc_restore_validation_status_datatype()}}, + { U("NcPropertyRestoreNoticeType"), {make_nc_property_restore_notice_type()}}, + { U("NcPropertyRestoreNotice"), {make_nc_property_restore_notice()}}, { U("NcObjectPropertiesSetValidation"), {make_nc_object_properties_set_validation_datatype()}}, { U("NcMethodResultBulkValuesHolder"), {make_nc_method_result_bulk_values_holder_datatype()}}, { U("NcMethodResultObjectPropertiesSetValidation"), {make_nc_method_result_object_properties_set_validation_datatype()}} diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index 13d45afa2..47caed188 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -340,8 +340,11 @@ namespace nmos const web::json::field_as_string validation_fingerprint{ U("validationFingerprint") }; const web::json::field_as_string status_message{ U("statusMessage") }; const web::json::field_as_value data_set{ U("dataSet") }; // NcBulkValuesHolder - const web::json::field_as_value traits{ U("traits") }; - const web::json::field_as_array included_property_traits{ U("includedPropertyTraits") }; + const web::json::field_as_bool is_rebuildable{ U("isRebuildable") }; + const web::json::field_as_integer notice_type{ U("noticeType") }; + const web::json::field_as_string notice_message{ U("noticeMessage") }; + const web::json::field_as_array notices{ U("notices") }; + const web::json::field_as_integer restore_mode{ U("restoreMode") }; } // NMOS Parameter Registers diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-request.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-request.json index 04c69a0e4..4603b2275 100644 --- a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-request.json +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-request.json @@ -1,15 +1,24 @@ { "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", - "description": "PUT request body for invoking SetPropertiesByPaths method on NcBulkPropertiesManager", - "title": "SetPropertiesByPaths", + "description": "PUT request body for invoking SetPropertiesByPath method on NcBulkPropertiesManager", + "title": "Bulk properties Set request", "required": [ "arguments" ], "properties": { "arguments": { "type": "object", - "description": "Method arguments. Arguments are specified as nested properties inside this object and their types are dictated by the specific MS-05-02 model for the method targeted. Arguments only need to be included for methods which have arguments and MUST be omitted if the method does not require any arguments." + "description": "Method arguments. The rolePath is offered in the URL and is not part of these arguments", + "properties": { + "dataSet": { + "type": "object", + "description": "NcBulkValuesHolder datatype" + }, + "recurse": { + "type": "boolean" + } + } } } -} +} \ No newline at end of file diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-request.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-request.json index a645997ff..7796c1ce1 100644 --- a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-request.json +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-request.json @@ -1,15 +1,24 @@ { "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", - "description": "PATCH request body for validating NcBulkValuesHolder object.", - "title": "ValidateSetPropertiesByPaths", + "description": "OPTIONS request body for invoking ValidateSetPropertiesByPath method on NcBulkPropertiesManager", + "title": "Bulk properties Validate request", "required": [ "arguments" ], "properties": { "arguments": { "type": "object", - "description": "Method arguments. Arguments are specified as nested properties inside this object and their types are dictated by the specific MS-05-02 model for the method targeted. Arguments only need to be included for methods which have arguments and MUST be omitted if the method does not require any arguments." + "description": "Method arguments. The rolePath is offered in the URL and is not part of these arguments", + "properties": { + "dataSet": { + "type": "object", + "description": "NcBulkValuesHolder datatype" + }, + "recurse": { + "type": "boolean" + } + } } } } diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/methods-base.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/methods-base.json index dd30ab1c7..6b9222b57 100644 --- a/Development/third_party/is-14/v1.0.x/APIs/schemas/methods-base.json +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/methods-base.json @@ -4,7 +4,8 @@ "description": "Describes the Configuration API /rolePaths/{rolePath}/methods base", "title": "Configuration API /rolePaths/{rolePath}/methods base", "items": { - "type": "string" + "type": "string", + "pattern": "^[0-9]+m[0-9]+" }, "uniqueItems": true } diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/properties-base.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/properties-base.json index ae1a93124..72c3f5488 100644 --- a/Development/third_party/is-14/v1.0.x/APIs/schemas/properties-base.json +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/properties-base.json @@ -4,7 +4,8 @@ "description": "Describes the Configuration API /rolePaths/{rolePath}/properties base", "title": "Configuration API /rolePaths/{rolePath}/properties base", "items": { - "type": "string" + "type": "string", + "pattern": "^[0-9]+p[0-9]+" }, "uniqueItems": true } diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePath.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePath.json index 0cafe9c28..03cf62b42 100644 --- a/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePath.json +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/rolePath.json @@ -6,8 +6,8 @@ "items": { "type": "string", "enum": [ - "bulkProperties", - "descriptors/", + "bulkProperties/", + "descriptor/", "methods/", "properties/" ] From 131ddc93087dcf73208d7081911894b3b9551bdc Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe <64410119+jonathan-r-thorpe@users.noreply.github.com> Date: Fri, 6 Dec 2024 10:41:08 +0000 Subject: [PATCH 129/250] Apply suggestions from code review Co-authored-by: Simon Lo --- Development/nmos/control_protocol_resource.cpp | 6 +++--- Development/nmos/control_protocol_resource.h | 4 ++-- Development/nmos/control_protocol_state.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index b79921579..9d866ea13 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -2177,7 +2177,7 @@ namespace nmos return details::make_nc_datatype_descriptor_enum(U("Restore validation status enumeration"), U("NcRestoreValidationStatus"), items, value::null()); } // TODO: add link - web::json::value make_nc_property_restore_notice_type() + web::json::value make_nc_property_restore_notice_type_datatype() { using web::json::value; @@ -2188,7 +2188,7 @@ namespace nmos return details::make_nc_datatype_descriptor_enum(U("Property restore notice type enumeration"), U("NcPropertyRestoreNoticeType"), items, value::null()); } // TODO: add link - web::json::value make_nc_property_restore_notice() + web::json::value make_nc_property_restore_notice_datatype() { using web::json::value; @@ -2198,7 +2198,7 @@ namespace nmos web::json::push_back(fields, details::make_nc_field_descriptor(U("Property restore notice type"), nmos::fields::nc::notice_type, U("NcPropertyRestoreNoticeType"), false, false, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Property restore notice message"), nmos::fields::nc::notice_message, U("NcString"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Bulk values holder descriptor"), U("NcPropertyRestoreNotice"), fields, value::null()); + return details::make_nc_datatype_descriptor_struct(U("Property restore notice descriptor"), U("NcPropertyRestoreNotice"), fields, value::null()); } // TODO: add link web::json::value make_nc_object_properties_set_validation_datatype() diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index b50bd4035..270dcc510 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -451,9 +451,9 @@ namespace nmos // web::json::value make_nc_restore_validation_status_datatype(); // - web::json::value make_nc_property_restore_notice_type(); + web::json::value make_nc_property_restore_notice_type_datatype(); // - web::json::value make_nc_property_restore_notice(); + web::json::value make_nc_property_restore_notice_datatype(); // web::json::value make_nc_object_properties_set_validation_datatype(); // diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 56542dd89..09ee8f6cc 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -486,8 +486,8 @@ namespace nmos { U("NcObjectPropertiesHolder"), {make_nc_object_properties_holder_datatype()}}, { U("NcBulkValuesHolder"), {make_nc_bulk_values_holder_datatype()}}, { U("NcRestoreValidationStatus"), {make_nc_restore_validation_status_datatype()}}, - { U("NcPropertyRestoreNoticeType"), {make_nc_property_restore_notice_type()}}, - { U("NcPropertyRestoreNotice"), {make_nc_property_restore_notice()}}, + { U("NcPropertyRestoreNoticeType"), {make_nc_property_restore_notice_type_datatype()}}, + { U("NcPropertyRestoreNotice"), {make_nc_property_restore_notice_datatype()}}, { U("NcObjectPropertiesSetValidation"), {make_nc_object_properties_set_validation_datatype()}}, { U("NcMethodResultBulkValuesHolder"), {make_nc_method_result_bulk_values_holder_datatype()}}, { U("NcMethodResultObjectPropertiesSetValidation"), {make_nc_method_result_object_properties_set_validation_datatype()}} From 6ad0c6752613059da3595c9ffd65d83b4b8be66a Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 29 Nov 2024 11:50:21 +0000 Subject: [PATCH 130/250] Remove property_trait enum --- Development/nmos/control_protocol_typedefs.h | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index 428036635..40301f0df 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -124,19 +124,6 @@ namespace nmos }; } - // Device Configuration - // NcPropertyTrait - namespace nc_property_trait - { - enum trait - { - instance_specific = 1, // Property is instance specific - ephemeral = 2, // Property is ephemeral - immutable = 3, // Property is immutable - device_generated = 4 // Property value is generated by the device - }; - } - // NcRestoreValidationStatus namespace nc_restore_validation_status { From 2722925ddf690e591bd0e3b77d67639b03eef799 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 9 Dec 2024 10:36:41 +0000 Subject: [PATCH 131/250] Initial backup implementation. --- .../nmos-cpp-node/node_implementation.cpp | 92 ++++++++++++++++++- Development/nmos/control_protocol_handlers.h | 6 +- .../nmos/control_protocol_resource.cpp | 14 +++ Development/nmos/control_protocol_resource.h | 3 + Development/nmos/control_protocol_state.cpp | 24 ++--- 5 files changed, 119 insertions(+), 20 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 12d4bd709..db3714195 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1719,22 +1719,104 @@ 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) +{ + 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)); + + while (!class_id.empty()) + { + // find the relevant nc_property_descriptor + const auto& control_class_descriptor = get_control_protocol_class_descriptor(class_id); + + for (const auto& property_descriptor : control_class_descriptor.property_descriptors.as_array()) + { + value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)), nmos::fields::nc::name(property_descriptor), nmos::fields::nc::type_name(property_descriptor), nmos::fields::nc::is_read_only(property_descriptor), resource.data.at(nmos::fields::nc::name(property_descriptor))); + + web::json::push_back(property_value_holders, property_value_holder); + } + class_id.pop_back(); + } + + 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))); + if (resources.end() == found) + { + break; + } + + found_resource = (*found); + role_path = nmos::fields::nc::role(found_resource.data) + U(".") + role_path; + 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); + + web::json::push_back(object_properties_holders, object_properties_holder); + + // Recurse into members + 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)) + { + const auto& members = nmos::fields::nc::members(resource.data); + + 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) + { + get_object_property_holder(resources, gate, get_control_protocol_class_descriptor, *found, recurse, object_properties_holders); + } + } + } + } + + return; +} + // 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) { - return [&resources, &gate](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, bool recurse) + 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, bool recurse) { + using web::json::value; + using web::json::value_of; + slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do get_properties_by_path"; - // Implement backup of device model here - return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }); + auto lock = control_protocol_state.read_lock(); + + value object_properties_holders = value::array(); + + 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); + + return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, bulk_values_holder); }; } // 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) { - return [&resources, &gate](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& restore_mode) + 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& restore_mode) { slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do validate_set_properties_by_path"; @@ -1746,7 +1828,7 @@ nmos::validate_set_properties_by_path_handler make_node_implementation_validate_ // 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::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::value& restore_mode) + 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::value& restore_mode) { slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do set_properties_by_path"; diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index 94c2c2b30..f487b0d83 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -38,9 +38,9 @@ namespace nmos // Device Configuration handlers // these callbacks should not throw exceptions - typedef std::function get_properties_by_path_handler; - typedef std::function validate_set_properties_by_path_handler; - typedef std::function set_properties_by_path_handler; + typedef std::function get_properties_by_path_handler; + typedef std::function validate_set_properties_by_path_handler; + typedef std::function set_properties_by_path_handler; namespace experimental { diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 9d866ea13..dbe58dcb0 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -827,6 +827,20 @@ namespace nmos return data; } + // TODO: add link + web::json::value make_nc_property_value_holder(const nc_property_id& property_id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, const web::json::value& property_value) + { + using web::json::value; + + return web::json::value_of({ + { nmos::fields::nc::id, make_nc_property_id(property_id)}, + { nmos::fields::nc::name, value::string(name)}, + { nmos::fields::nc::type_name, value::string(type_name)}, + { nmos::fields::nc::is_read_only, value::boolean(is_read_only)}, + { nmos::fields::nc::value, property_value}, + }, true); + } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertychangedeventdata web::json::value make_nc_property_changed_event_data(const nc_property_changed_event_data& property_changed_event_data) { diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 270dcc510..a5463de6a 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -194,6 +194,9 @@ namespace nmos // TODO: add link web::json::value make_nc_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); + + // TODO: add link + web::json::value make_nc_property_value_holder(const nc_property_id& property_id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, const web::json::value& property_value); } // command message response diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 09ee8f6cc..50c494140 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -179,9 +179,9 @@ namespace nmos return get_datatype(arguments, is_deprecated, get_control_protocol_datatype_descriptor, gate); }; } - nmos::experimental::control_protocol_method_handler make_nc_get_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_properties_by_path_handler get_properties_by_path) + nmos::experimental::control_protocol_method_handler make_nc_get_properties_by_path_handler(nmos::experimental::control_protocol_state& control_protocol_state, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_properties_by_path_handler get_properties_by_path) { - return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [&control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); @@ -190,7 +190,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); if (get_properties_by_path) { - result = get_properties_by_path(get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, recurse); + result = get_properties_by_path(control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, recurse); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -201,9 +201,9 @@ namespace nmos return result; }; } - nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, validate_set_properties_by_path_handler validate_set_properties_by_path) + nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(nmos::experimental::control_protocol_state& control_protocol_state, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, validate_set_properties_by_path_handler validate_set_properties_by_path) { - return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_set_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [&control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_set_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -217,7 +217,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); if (validate_set_properties_by_path) { - result = validate_set_properties_by_path(get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, data_set, recurse, restore_mode); + result = validate_set_properties_by_path(control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, data_set, recurse, restore_mode); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -228,9 +228,9 @@ namespace nmos return result; }; } - nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, set_properties_by_path_handler set_properties_by_path) + nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(nmos::experimental::control_protocol_state& control_protocol_state, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, set_properties_by_path_handler set_properties_by_path) { - return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, set_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [&control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, set_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -244,7 +244,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); if (set_properties_by_path) { - result = set_properties_by_path(get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, data_set, recurse, restore_mode); + result = set_properties_by_path(control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, data_set, recurse, restore_mode); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -395,9 +395,9 @@ namespace nmos to_vector(make_nc_bulk_properties_manager_properties()), to_methods_vector(make_nc_bulk_properties_manager_methods(), { - { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), get_properties_by_path)}, - { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), validate_set_properties_by_path) }, - { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), set_properties_by_path) } + { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(*this, make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), get_properties_by_path)}, + { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(*this, make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), validate_set_properties_by_path) }, + { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(*this, make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), set_properties_by_path) } }), to_vector(make_nc_bulk_properties_manager_events())) } }; From 16afabc514af02af880feb5fecc72d7a274f1f06 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 9 Dec 2024 11:37:16 +0000 Subject: [PATCH 132/250] Add is_rebuildable flag --- Development/nmos-cpp-node/node_implementation.cpp | 3 ++- Development/nmos/control_protocol_resource.cpp | 4 +++- Development/nmos/control_protocol_resource.h | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index db3714195..bac39a313 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1761,7 +1761,8 @@ void get_object_property_holder(const nmos::resources& resources, slog::base_gat auto object_properties_holder = web::json::value_of({ { nmos::fields::nc::path, role_path }, - { nmos::fields::nc::values, property_value_holders} + { nmos::fields::nc::values, property_value_holders}, + { nmos::fields::nc::is_rebuildable, nmos::fields::nc::is_rebuildable(resource.data)} }, true); web::json::push_back(object_properties_holders, object_properties_holder); diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index dbe58dcb0..91731aaba 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -697,7 +697,7 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject - web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool is_rebuildable) { using web::json::value; @@ -712,6 +712,8 @@ namespace nmos data[nmos::fields::nc::touchpoints] = touchpoints; data[nmos::fields::nc::runtime_property_constraints] = runtime_property_constraints; // level 2 runtime constraints. See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + // IS-14 isRebuilable flag + data[nmos::fields::nc::is_rebuildable] = value::boolean(is_rebuildable); return data; } diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index a5463de6a..f17d654fd 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -169,7 +169,7 @@ namespace nmos web::json::value make_nc_touchpoint_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject - web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); + web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool is_rebuildable=false); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members); From 9f3975524900de26dc90776844167dd0c466454f Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 9 Dec 2024 11:48:05 +0000 Subject: [PATCH 133/250] Rename populate_object_properties_holder function --- .../nmos-cpp-node/node_implementation.cpp | 155 +++++++++++++++--- Development/nmos/configuration_api.cpp | 110 ++----------- .../nmos/control_protocol_resource.cpp | 80 +++++++-- Development/nmos/control_protocol_resource.h | 12 ++ Development/nmos/control_protocol_typedefs.h | 17 +- Development/nmos/control_protocol_utils.cpp | 134 +++++++++++++-- Development/nmos/control_protocol_utils.h | 6 + Development/nmos/json_fields.h | 2 +- 8 files changed, 361 insertions(+), 155 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index bac39a313..219df195f 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1,5 +1,6 @@ #include "node_implementation.h" +#include #include #include #include @@ -1719,7 +1720,7 @@ 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; @@ -1727,9 +1728,9 @@ void get_object_property_holder(const nmos::resources& resources, slog::base_gat nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + // make NcPropertyValueHolder objects while (!class_id.empty()) { - // find the relevant nc_property_descriptor const auto& control_class_descriptor = get_control_protocol_class_descriptor(class_id); for (const auto& property_descriptor : control_class_descriptor.property_descriptors.as_array()) @@ -1740,53 +1741,106 @@ 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}, - { nmos::fields::nc::is_rebuildable, nmos::fields::nc::is_rebuildable(resource.data)} - }, true); + std::reverse(role_path.as_array().begin(), role_path.as_array().end()); + + return role_path; +} + +void populate_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, nmos::fields::nc::is_rebuildable(resource.data)); 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)) { const auto& members = nmos::fields::nc::members(resource.data); + for (const auto& member : members) + { + const auto& found = find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(member)))); + + if (resources.end() != found) + { + populate_object_property_holder(resources, gate, get_control_protocol_class_descriptor, *found, recurse, object_properties_holders); + } + } + } + } + + 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) { - get_object_property_holder(resources, gate, get_control_protocol_class_descriptor, *found, recurse, object_properties_holders); + size_t sub_hash = generate_validation_fingerprint(resources, *found); + boost::hash_combine(hash, sub_hash); } } } } - return; + return hash; } // Example Device Configuration callback for creating a back-up dataset @@ -1803,38 +1857,89 @@ nmos::get_properties_by_path_handler make_node_implementation_get_properties_by_ value object_properties_holders = value::array(); - get_object_property_holder(resources, gate, get_control_protocol_class_descriptor, resource, recurse, object_properties_holders); + populate_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>(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::value& restore_mode) { slog::log(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, restore_mode, 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::value& restore_mode) + 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& restore_mode) { slog::log(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, restore_mode, false); }; } diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 26fa3b717..707d70c98 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -1,6 +1,5 @@ #include "nmos/configuration_api.h" -#include #include #include #include "cpprest/json_validator.h" @@ -84,85 +83,6 @@ namespace nmos } } - web::json::value get_nc_block_member_descriptor(const resources& resources, const nmos::resource& parent_nc_block_resource, std::list& role_path_segments) - { - if (parent_nc_block_resource.data.has_field(nmos::fields::nc::members)) - { - const auto& members = nmos::fields::nc::members(parent_nc_block_resource.data); - - const auto role_path_segement = role_path_segments.front(); - role_path_segments.pop_front(); - // find the role_path_segment member - auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& member) - { - return role_path_segement == nmos::fields::nc::role(member); - }); - - if (members.end() != member_found) - { - if (role_path_segments.empty()) - { - // NcBlockMemberDescriptor - return *member_found; - } - - // get the role_path_segement member resource - if (is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(*member_found)))) - { - // get resource based on the oid - const auto& oid = nmos::fields::nc::oid(*member_found); - const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found) - { - return get_nc_block_member_descriptor(resources, *found, role_path_segments); - } - } - } - } - return web::json::value{}; - } - - resources::const_iterator find_resource(const resources& resources, std::list& role_path_segments) - { - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); - if (resources.end() != resource) - { - const auto role = nmos::fields::nc::role(resource->data); - if (role_path_segments.size() && role == role_path_segments.front()) - { - role_path_segments.pop_front(); - - if (role_path_segments.size()) - { - const auto& block_member_descriptor = details::get_nc_block_member_descriptor(resources, *resource, role_path_segments); - if (!block_member_descriptor.is_null()) - { - const auto& oid = nmos::fields::nc::oid(block_member_descriptor); - const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found) - { - return found; - } - } - } - else - { - return resource; - } - } - } - return resources.end(); - } - - resources::const_iterator find_resource(const resources& resources, const utility::string_t& role_path) - { - // tokenize the role_path with the '.' delimiter - std::list role_path_segments; - boost::algorithm::split(role_path_segments, role_path, [](utility::char_t c) { return '.' == c; }); - - return find_resource(resources, role_path_segments); - } - nc_property_id parse_formatted_property_id(const utility::string_t& property_id) { // Assume that property_id is in form "p" as validated by the propertyId regular expression pattern @@ -272,7 +192,7 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = details::find_resource(resources, role_path); + const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); if (resources.end() != resource) { @@ -294,7 +214,7 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = details::find_resource(resources, role_path); + const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); if (resources.end() != resource) { std::set properties_routes; @@ -333,7 +253,7 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = details::find_resource(resources, role_path); + const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); if (resources.end() != resource) { std::set methods_routes; @@ -379,7 +299,7 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = details::find_resource(resources, role_path); + const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); if (resources.end() != resource) { nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); @@ -435,7 +355,7 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = details::find_resource(resources, role_path); + const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); if (resources.end() != resource) { // find the relevant nc_property_descriptor @@ -466,7 +386,7 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = details::find_resource(resources, role_path); + const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); if (resources.end() != resource) { // find the relevant nc_property_descriptor @@ -498,7 +418,7 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = details::find_resource(resources, role_path); + const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); if (resources.end() != resource) { // find the relevant nc_property_descriptor @@ -540,7 +460,7 @@ namespace nmos auto& resources = model.control_protocol_resources; auto& arguments = nmos::fields::nc::arguments(body); - const auto& resource = details::find_resource(resources, role_path); + const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); if (resources.end() != resource) { auto method = get_control_protocol_method_descriptor(details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), details::parse_formatted_method_id(method_id)); @@ -614,7 +534,7 @@ namespace nmos auto& resources = model.control_protocol_resources; - const auto& resource = details::find_resource(resources, role_path); + const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); if (resources.end() != resource) { // find the relevant nc_property_descriptor @@ -653,8 +573,8 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = details::find_resource(resources, role_path); - const auto& bulk_properties_manager = details::find_resource(resources, nmos::bulk_properties_manager_role); + const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); + const auto& bulk_properties_manager = find_control_protocol_resource_by_role_path(resources, nmos::bulk_properties_manager_role); if (resources.end() != resource && resources.end() != bulk_properties_manager) { @@ -721,8 +641,8 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = details::find_resource(resources, role_path); - const auto& bulk_properties_manager = details::find_resource(resources, nmos::bulk_properties_manager_role); + const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); + const auto& bulk_properties_manager = find_control_protocol_resource_by_role_path(resources, nmos::bulk_properties_manager_role); if (resources.end() != resource && resources.end() != bulk_properties_manager) { return details::extract_json(req, gate_).then([res, resources, resource, get_control_protocol_method_descriptor, version, &gate_](value body) mutable @@ -794,8 +714,8 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = details::find_resource(resources, role_path); - const auto& bulk_properties_manager = details::find_resource(resources, nmos::bulk_properties_manager_role); + const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); + const auto& bulk_properties_manager = find_control_protocol_resource_by_role_path(resources, nmos::bulk_properties_manager_role); if (resources.end() != resource && resources.end() != bulk_properties_manager) { return details::extract_json(req, gate_).then([res, resources, resource, get_control_protocol_method_descriptor, version, &gate_](value body) mutable diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 91731aaba..cb43dfb64 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -819,6 +819,20 @@ namespace nmos return data; } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertychangedeventdata + web::json::value make_nc_property_changed_event_data(const nc_property_changed_event_data& property_changed_event_data) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::property_id, details::make_nc_property_id(property_changed_event_data.property_id) }, + { nmos::fields::nc::change_type, property_changed_event_data.change_type }, + { nmos::fields::nc::value, property_changed_event_data.value }, + { nmos::fields::nc::sequence_item_index, property_changed_event_data.sequence_item_index } + }, true + ); + } + // TODO: add link web::json::value make_nc_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value &user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { @@ -829,12 +843,24 @@ namespace nmos return data; } + web::json::value make_nc_bulk_values_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::validation_fingerprint, validation_fingerprint }, + { nmos::fields::nc::values, object_properties_holders } + }, true + ); + } + // TODO: add link web::json::value make_nc_property_value_holder(const nc_property_id& property_id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, const web::json::value& property_value) { - using web::json::value; + using web::json::value; + using web::json::value_of; - return web::json::value_of({ + return value_of({ { nmos::fields::nc::id, make_nc_property_id(property_id)}, { nmos::fields::nc::name, value::string(name)}, { nmos::fields::nc::type_name, value::string(type_name)}, @@ -843,17 +869,48 @@ namespace nmos }, true); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertychangedeventdata - web::json::value make_nc_property_changed_event_data(const nc_property_changed_event_data& property_changed_event_data) + // TODO: add link + web::json::value make_nc_object_properties_holder(const web::json::value& role_path, const web::json::value& property_value_holders, bool is_rebuildable) { using web::json::value_of; return value_of({ - { nmos::fields::nc::property_id, details::make_nc_property_id(property_changed_event_data.property_id) }, - { nmos::fields::nc::change_type, property_changed_event_data.change_type }, - { nmos::fields::nc::value, property_changed_event_data.value }, - { nmos::fields::nc::sequence_item_index, property_changed_event_data.sequence_item_index } - }); + { nmos::fields::nc::path, role_path }, + { nmos::fields::nc::values, property_value_holders}, + { nmos::fields::nc::is_rebuildable, is_rebuildable} + }, true + ); + + } + + // TODO: add link + web::json::value make_nc_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message) + { + using web::json::value; + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::id, make_nc_property_id(property_id)}, + { nmos::fields::nc::name, value::string(name)}, + { nmos::fields::nc::notice_type, value::number(notice_type)}, + { nmos::fields::nc::notice_message, value::string(notice_message)} + }, true + ); + } + + // TODO: add link + web::json::value make_nc_object_properties_set_validation(const web::json::value& role_path, nc_restore_validation_status::status status, const web::json::value& notices, const utility::string_t& status_message) + { + using web::json::value; + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::path, role_path}, + { nmos::fields::nc::status, value::number(status)}, + { nmos::fields::nc::notices, notices }, + { nmos::fields::nc::status_message, value::string(status_message)} + }, true + ); } } @@ -2198,9 +2255,8 @@ namespace nmos using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Warning property restore notice"), U("Warning"), 300)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Error property restore notice"), U("Error"), 400)); - + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Warning property restore notice"), U("Warning"), nc_property_restore_notice_type::warning)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Error property restore notice"), U("Error"), nc_property_restore_notice_type::error)); return details::make_nc_datatype_descriptor_enum(U("Property restore notice type enumeration"), U("NcPropertyRestoreNoticeType"), items, value::null()); } // TODO: add link diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index f17d654fd..b748f4c36 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -195,8 +195,20 @@ namespace nmos // TODO: add link web::json::value make_nc_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); + // TODO: add link + web::json::value make_nc_bulk_values_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders); + // TODO: add link web::json::value make_nc_property_value_holder(const nc_property_id& property_id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, const web::json::value& property_value); + + // TODO: add link + web::json::value make_nc_object_properties_holder(const web::json::value& role_path, const web::json::value& property_value_holders, bool is_rebuildable); + + // TODO: add link + web::json::value make_nc_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message); + + // TODO: add link + web::json::value make_nc_object_properties_set_validation(const web::json::value& role_path, nc_restore_validation_status::status status, const web::json::value& notices, const utility::string_t& status_message); } // command message response diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index 40301f0df..0542ca0eb 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -127,17 +127,26 @@ namespace nmos // NcRestoreValidationStatus namespace nc_restore_validation_status { - enum staus + enum status { ok = 200, // Restore was successful - excluded = 204, // Excluded from restore due to data provided in the request - invalid_data = 400, // Restore failed because relevant backup data set provided is invalid + excluded = 210, // Excluded from restore due to data provided in the request + failed = 400, // Restore failed because relevant backup data set provided is invalid not_found = 404, // Restore failed because the role path is not found in the device model or the device cannot create the role path from the data set - missing_dependency = 424, // Restore failed because of missing dependency information in the relevant backup data set device_error = 500 // Restore failed due to an internal device error preventing the restore from happening }; } + // NcPropertyRestoreNoticeType + namespace nc_property_restore_notice_type + { + enum type + { + warning = 300, // Warning property restore notice + error = 400 // Error property restore notice + }; + }; + // NcElementId // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncelementid struct nc_element_id diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 1acff9d65..65f245b70 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include "bst/regex.h" #include "cpprest/json_utils.h" @@ -334,6 +335,70 @@ namespace nmos // do level 1 property constraints & level 0 datatype constraints validation constraints_validation(data, value::null(), property_constraints, params); } + + web::json::value get_nc_block_member_descriptor(const resources& resources, const nmos::resource& parent_nc_block_resource, web::json::value& role_path_segments) + { + if (parent_nc_block_resource.data.has_field(nmos::fields::nc::members)) + { + const auto& members = nmos::fields::nc::members(parent_nc_block_resource.data); + + + const auto role_path_segement = web::json::front(role_path_segments); + role_path_segments.erase(0); + // find the role_path_segment member + auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& member) + { + return role_path_segement.as_string() == nmos::fields::nc::role(member); + }); + + if (members.end() != member_found) + { + if (role_path_segments.size() == 0) + { + // NcBlockMemberDescriptor + return *member_found; + } + + // get the role_path_segement member resource + if (is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(*member_found)))) + { + // get resource based on the oid + const auto& oid = nmos::fields::nc::oid(*member_found); + const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + return get_nc_block_member_descriptor(resources, *found, role_path_segments); + } + } + } + } + return web::json::value{}; + } + + typedef std::function get_property_descriptors_handler; + + // generic find control class property descriptor in property_descriptor_ array (NcPropertyDescriptor) + web::json::value find_property_descriptor(const nc_property_id& property_id, const nc_class_id& class_id_, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + { + using web::json::value; + + auto class_id = class_id_; + + while (!class_id.empty()) + { + const auto& control_class = get_control_protocol_class_descriptor(class_id); + const auto& property_descriptors = control_class.property_descriptors; + auto found = std::find_if(property_descriptors.as_array().begin(), property_descriptors.as_array().end(), [&property_id](const web::json::value& property_descriptor) + { + return (property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor))); + }); + if (property_descriptors.as_array().end() != found) { return *found; } + + class_id.pop_back(); + } + + return value::null(); + } } // is the given class_id a NcBlock @@ -382,24 +447,7 @@ namespace nmos // find control class property descriptor (NcPropertyDescriptor) web::json::value find_property_descriptor(const nc_property_id& property_id, const nc_class_id& class_id_, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { - using web::json::value; - - auto class_id = class_id_; - - while (!class_id.empty()) - { - const auto& control_class = get_control_protocol_class_descriptor(class_id); - auto& property_descriptors = control_class.property_descriptors.as_array(); - auto found = std::find_if(property_descriptors.begin(), property_descriptors.end(), [&property_id](const web::json::value& property_descriptor) - { - return (property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor))); - }); - if (property_descriptors.end() != found) { return *found; } - - class_id.pop_back(); - } - - return value::null(); + return details::find_property_descriptor(property_id, class_id_, get_control_protocol_class_descriptor); } // get block member descriptors @@ -637,4 +685,54 @@ namespace nmos details::method_parameter_constraints_validation(arguments.at(name), constraints, { nmos::details::get_datatype_descriptor(type_name, get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); } } + + resources::const_iterator find_control_protocol_resource_by_role_path(const resources& resources, const web::json::value& role_path_) + { + web::json::value role_path = role_path_; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); + if (resources.end() != resource) + { + const auto role = nmos::fields::nc::role(resource->data); + + if (role_path.size() && role == web::json::front(role_path).as_string()) + { + role_path.erase(0); + + if (role_path.size()) + { + const auto& block_member_descriptor = details::get_nc_block_member_descriptor(resources, *resource, role_path); + if (!block_member_descriptor.is_null()) + { + const auto& oid = nmos::fields::nc::oid(block_member_descriptor); + const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + return found; + } + } + } + else + { + return resource; + } + } + } + return resources.end(); + } + + + resources::const_iterator find_control_protocol_resource_by_role_path(const resources& resources, const utility::string_t& role_path_) + { + // tokenize the role_path with the '.' delimiter + std::list role_path_segments; + boost::algorithm::split(role_path_segments, role_path_, [](utility::char_t c) { return '.' == c; }); + + web::json::value role_path = web::json::value::array(); + + for (auto item : role_path_segments) + { + web::json::push_back(role_path, utility::string_t(item.c_str())); + } + return find_control_protocol_resource_by_role_path(resources, role_path); + } } diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index 6d7060851..4403a74b4 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -77,6 +77,12 @@ namespace nmos // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id resources::const_iterator find_control_protocol_resource(resources& resources, type type, const id& id); + // find resource based on role path. + resources::const_iterator find_control_protocol_resource_by_role_path(const resources& resources, const web::json::value& role_path); + + // find resource based on role path. Roles in role path string must be delimited with a '.' + resources::const_iterator find_control_protocol_resource_by_role_path(const resources& resources, const utility::string_t& role_path); + // method parameters constraints validation, may throw nmos::control_protocol_exception void method_parameters_contraints_validation(const web::json::value& arguments, const web::json::value& nc_method_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor); } diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index 47caed188..58aa5915c 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -312,7 +312,7 @@ namespace nmos const web::json::field_as_array fields{ U("fields") }; // sequence const web::json::field_as_integer generic_state{ U("generic") }; // NcDeviceGenericState const web::json::field_as_string device_specific_details{ U("deviceSpecificDetails") }; - const web::json::field_as_array path{ U("path") }; // NcRolePath + const web::json::field_as_value path{ U("path") }; // NcRolePath const web::json::field_as_bool case_sensitive{ U("caseSensitive") }; const web::json::field_as_bool match_whole_string{ U("matchWholeString") }; const web::json::field_as_bool include_derived{ U("includeDerived") }; From 3d4a2a63512ac52624c5447791410ae237171887 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 18 Dec 2024 16:41:06 +0000 Subject: [PATCH 134/250] Validate and set backup dataset methods --- Development/cmake/NmosCppLibraries.cmake | 3 + Development/cmake/NmosCppTest.cmake | 1 + Development/nmos-cpp-node/main.cpp | 2 +- .../nmos-cpp-node/node_implementation.cpp | 214 +------- Development/nmos/configuration_handlers.h | 30 + Development/nmos/configuration_methods.cpp | 454 +++++++++++++++ Development/nmos/configuration_methods.h | 35 ++ .../nmos/control_protocol_resource.cpp | 4 +- Development/nmos/control_protocol_resource.h | 2 +- .../nmos/control_protocol_resources.cpp | 10 +- Development/nmos/control_protocol_resources.h | 2 +- Development/nmos/control_protocol_state.cpp | 25 +- Development/nmos/control_protocol_state.h | 3 +- Development/nmos/control_protocol_typedefs.h | 11 +- Development/nmos/control_protocol_utils.cpp | 11 +- Development/nmos/control_protocol_utils.h | 3 + Development/nmos/node_server.h | 15 +- .../nmos/test/configuration_methods_test.cpp | 518 ++++++++++++++++++ 18 files changed, 1109 insertions(+), 234 deletions(-) create mode 100644 Development/nmos/configuration_handlers.h create mode 100644 Development/nmos/configuration_methods.cpp create mode 100644 Development/nmos/configuration_methods.h create mode 100644 Development/nmos/test/configuration_methods_test.cpp diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index 12ce63b2c..e71af5ef2 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -1006,6 +1006,7 @@ set(NMOS_CPP_NMOS_SOURCES nmos/client_utils.cpp nmos/components.cpp nmos/configuration_api.cpp + nmos/configuration_methods.cpp nmos/connection_activation.cpp nmos/connection_api.cpp nmos/connection_events_activation.cpp @@ -1100,6 +1101,8 @@ set(NMOS_CPP_NMOS_HEADERS nmos/components.h nmos/copyable_atomic.h nmos/configuration_api.h + nmos/configuration_handlers.h + nmos/configuration_methods.h nmos/connection_activation.h nmos/connection_api.h nmos/connection_events_activation.h diff --git a/Development/cmake/NmosCppTest.cmake b/Development/cmake/NmosCppTest.cmake index 8dc26b18c..4fd2fc00b 100644 --- a/Development/cmake/NmosCppTest.cmake +++ b/Development/cmake/NmosCppTest.cmake @@ -43,6 +43,7 @@ set(NMOS_CPP_TEST_NMOS_TEST_SOURCES nmos/test/api_utils_test.cpp nmos/test/capabilities_test.cpp nmos/test/channels_test.cpp + nmos/test/configuration_methods_test.cpp nmos/test/control_protocol_test.cpp nmos/test/did_sdid_test.cpp nmos/test/event_type_test.cpp diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index 51a7dec13..290ca17c6 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -138,7 +138,7 @@ int main(int argc, char* argv[]) .on_request_authorization_code(nmos::experimental::make_request_authorization_code_handler(gate)); // may be omitted, only required for OAuth client which is using the Authorization Code Flow to obtain the access token } - nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.get_properties_by_path, node_implementation.validate_set_properties_by_path, node_implementation.set_properties_by_path); + nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.get_properties_by_path, node_implementation.modify_read_only_config_properties, node_implementation.modify_rebuildable_block); if (0 <= nmos::fields::control_protocol_ws_port(node_model.settings)) { node_implementation diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 219df195f..3ea68451d 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -20,6 +20,8 @@ #include "nmos/channelmapping_resources.h" #include "nmos/clock_name.h" #include "nmos/colorspace.h" +#include "nmos/configuration_handlers.h" +#include "nmos/configuration_methods.h" #include "nmos/connection_resources.h" #include "nmos/connection_events_activation.h" #include "nmos/control_protocol_resources.h" @@ -1720,226 +1722,36 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control }; } -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)); - - // make NcPropertyValueHolder objects - while (!class_id.empty()) - { - const auto& control_class_descriptor = get_control_protocol_class_descriptor(class_id); - - for (const auto& property_descriptor : control_class_descriptor.property_descriptors.as_array()) - { - value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)), nmos::fields::nc::name(property_descriptor), nmos::fields::nc::type_name(property_descriptor), nmos::fields::nc::is_read_only(property_descriptor), resource.data.at(nmos::fields::nc::name(property_descriptor))); - - web::json::push_back(property_value_holders, property_value_holder); - } - 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 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()) - { - 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); - web::json::push_back(role_path, nmos::fields::nc::role(found_resource.data)); - oid = nmos::fields::nc::id(found_resource.data); - } - - std::reverse(role_path.as_array().begin(), role_path.as_array().end()); - - return role_path; -} - -void populate_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, nmos::fields::nc::is_rebuildable(resource.data)); - - web::json::push_back(object_properties_holders, object_properties_holder); - - // 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)) - { - const auto& members = nmos::fields::nc::members(resource.data); - - for (const auto& member : members) - { - const auto& found = find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(member)))); - - if (resources.end() != found) - { - populate_object_property_holder(resources, gate, get_control_protocol_class_descriptor, *found, recurse, object_properties_holders); - } - } - } - } - - 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) { 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, bool recurse) { - using web::json::value; - using web::json::value_of; - slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do get_properties_by_path"; - auto lock = control_protocol_state.read_lock(); - - value object_properties_holders = value::array(); - - populate_object_property_holder(resources, gate, get_control_protocol_class_descriptor, resource, recurse, object_properties_holders); - - 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); + return nmos::get_properties_by_path(resources, control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, recurse); }; } -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>(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(nmos::resources& resources, slog::base_gate& gate) +nmos::modify_read_only_config_properties_handler make_modify_read_only_config_properties_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::value& restore_mode) + return [&resources, &gate](nmos::experimental::control_protocol_state& control_protocol_state, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const web::json::value& target_role_path, const web::json::value& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate) { - slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do validate_set_properties_by_path"; + slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do modify_read_only_config_properties"; - return apply_backup_data_set(resources, control_protocol_state, get_control_protocol_class_descriptor, resource, backup_data_set, recurse, restore_mode, true); + return web::json::value(); }; } // 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) +nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_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::value& restore_mode) + return [&resources, &gate](nmos::experimental::control_protocol_state& control_protocol_state, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const web::json::value& target_role_path, const web::json::value& property_values, bool recurse, const web::json::value& restore_mode, bool validate) { - slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do set_properties_by_path"; + slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do modify_rebuildable_block"; - return apply_backup_data_set(resources, control_protocol_state, get_control_protocol_class_descriptor, resource, backup_data_set, recurse, restore_mode, false); + return web::json::value(); }; } @@ -2099,6 +1911,6 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode .on_channelmapping_activated(make_node_implementation_channelmapping_activation_handler(gate)) .on_control_protocol_property_changed(make_node_implementation_control_protocol_property_changed_handler(gate)) // may be omitted if IS-12 not required .on_get_properties_by_path(make_node_implementation_get_properties_by_path_handler(model.control_protocol_resources, gate)) // may be omitted if IS-14 not required - .on_validate_set_properties_by_path(make_node_implementation_validate_set_properties_by_path_handler(model.control_protocol_resources, gate)) // may be omitted if IS-14 not required - .on_set_properties_by_path(make_node_implementation_set_properties_by_path_handler(model.control_protocol_resources, gate)); // may be omitted if IS-14 not required + .on_modify_read_only_config_properties(make_modify_read_only_config_properties_handler(model.control_protocol_resources, gate)) // may be omitted if IS-14 not required + .on_modify_rebuildable_block(make_modify_rebuildable_block_handler(model.control_protocol_resources, gate)); // may be omitted if IS-14 not required } diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h new file mode 100644 index 000000000..f6d4258cc --- /dev/null +++ b/Development/nmos/configuration_handlers.h @@ -0,0 +1,30 @@ +#ifndef NMOS_CONFIGURATION_HANDLERS_H +#define NMOS_CONFIGURATION_HANDLERS_H + +#include +#include "nmos/control_protocol_typedefs.h" +#include "nmos/control_protocol_handlers.h" +#include "nmos/resources.h" + +namespace slog +{ + class base_gate; +} + +namespace nmos +{ + namespace experimental + { + struct control_protocol_state; + } + // This callback is invoked if attempting to modify read only properties when restoring a configuration. + // This function should modify the Device Model object directly and return a corresponding NcObjectPropertiesSetValidation object + typedef std::function modify_read_only_config_properties_handler; + + // This callback is invoked if attempting to modify a rebuildable block when restoring a configuration. + // This function should handle the modification of the Device Model and any corresponding NMOS resources + // and return correpsonding NcObjectPropertiesSetValidation objects for each object modified/added + typedef std::function modify_rebuildable_block_handler; +} + +#endif diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp new file mode 100644 index 000000000..d69406cbd --- /dev/null +++ b/Development/nmos/configuration_methods.cpp @@ -0,0 +1,454 @@ +#include "nmos/configuration_methods.h" + +#include +#include "cpprest/json_utils.h" +#include "nmos/configuration_handlers.h" +#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_resources.h" +#include "nmos/control_protocol_state.h" +#include "nmos/control_protocol_utils.h" +#include "nmos/slog.h" + +namespace nmos +{ + namespace details + { + 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)); + + // make NcPropertyValueHolder objects + while (!class_id.empty()) + { + const auto& control_class_descriptor = get_control_protocol_class_descriptor(class_id); + + for (const auto& property_descriptor : control_class_descriptor.property_descriptors.as_array()) + { + value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)), nmos::fields::nc::name(property_descriptor), nmos::fields::nc::type_name(property_descriptor), nmos::fields::nc::is_read_only(property_descriptor), resource.data.at(nmos::fields::nc::name(property_descriptor))); + + web::json::push_back(property_value_holders, property_value_holder); + } + class_id.pop_back(); + } + return property_value_holders; + } + + void populate_object_property_holder(const nmos::resources& resources, 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, nmos::fields::nc::is_rebuildable(resource.data)); + + web::json::push_back(object_properties_holders, object_properties_holder); + + // 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)) + { + const auto& members = nmos::fields::nc::members(resource.data); + + for (const auto& member : members) + { + const auto& found = find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(member)))); + + if (resources.end() != found) + { + populate_object_property_holder(resources, get_control_protocol_class_descriptor, *found, recurse, object_properties_holders); + } + } + } + } + + 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; + } + } + + 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 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()) + { + 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); + web::json::push_back(role_path, nmos::fields::nc::role(found_resource.data)); + oid = nmos::fields::nc::id(found_resource.data); + } + + std::reverse(role_path.as_array().begin(), role_path.as_array().end()); + + return role_path; + } + + // Check to see if root_role_path is root of role_path_ + bool is_role_path_root(const web::json::value& role_path_root, const web::json::value& role_path_) + { + if (role_path_root.as_array().size() > role_path_.as_array().size()) + { + // root can't be longed that the path + return false; + } + for (int i = 0; i < role_path_root.as_array().size(); ++i) + { + if (role_path_root.as_array().at(i) != role_path_.as_array().at(i)) + { + return false; + } + } + return true; + } + + bool is_block_modified(const nmos::resource& resource, const web::json::value& object_properties_holder) + { + // Are they blocks? + nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + if (!nmos::is_nc_block(class_id)) + { + return false; + } + const auto& block_members_properties_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) + | boost::adaptors::filtered([](const web::json::value& property_value_holder) + { + return nmos::nc_property_id(2, 2) == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value_holder)); + }) + ); + // There should only be a single property holder for the members + if (block_members_properties_holders.size() == 1) + { + const auto& members_property_holder = *block_members_properties_holders.begin(); + const auto& restore_members = nmos::fields::nc::value(members_property_holder); + const auto& reference_members = nmos::fields::nc::members(resource.data); + + if (reference_members.size() != restore_members.as_array().size()) + { + return true; + } + for (const auto& reference_member : reference_members) + { + const auto& filtered_members = boost::copy_range>(restore_members.as_array() + | boost::adaptors::filtered([&reference_member](const web::json::value& member) + { + return nmos::fields::nc::oid(reference_member) == nmos::fields::nc::oid(member); + }) + ); + if (filtered_members.size() != 1) + { + // can't find this oid, so member has been removed + return true; + } + const auto restore_member = *filtered_members.begin(); + // We ignore the description and user label as these are non-normative + if (nmos::fields::nc::role(reference_member) != nmos::fields::nc::role(restore_member) + || nmos::fields::nc::constant_oid(reference_member) != nmos::fields::nc::constant_oid(restore_member) + || nmos::fields::nc::class_id(reference_member) != nmos::fields::nc::class_id(restore_member) + || nmos::fields::nc::owner(reference_member) != nmos::fields::nc::owner(restore_member)) + { + return true; + } + } + } + + return false; + } + + web::json::value get_properties_by_path(const nmos::resources& resources, 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, bool recurse) + { + using web::json::value; + using web::json::value_of; + + auto lock = control_protocol_state.read_lock(); + + value object_properties_holders = value::array(); + + details::populate_object_property_holder(resources, get_control_protocol_class_descriptor, resource, recurse, object_properties_holders); + + size_t validation_fingerprint = details::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 check_property_value(const web::json::value& property_value, const web::json::value& property_descriptor, const web::json::value& restore_mode) + { + const nmos::nc_property_id& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)); + + auto property_restore_notices = web::json::value::array(); + + // Check the name of the property is correct + if (nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::name(property_value)) + { + utility::ostringstream_t os; + os << U("unexpected property name: expected ") << nmos::fields::nc::name(property_descriptor) << U(", actual ") << nmos::fields::nc::name(property_value); + 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::error, os.str()); + web::json::push_back(property_restore_notices, property_restore_notice); + } + // Check the type of the property value is correct + if (nmos::fields::nc::type_name(property_descriptor) != nmos::fields::nc::type_name(property_value)) + { + utility::ostringstream_t os; + os << U("unexpected property type: expected ") << nmos::fields::nc::type_name(property_descriptor) << U(", actual ") << nmos::fields::nc::type_name(property_value); + 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::error, os.str()); + web::json::push_back(property_restore_notices, property_restore_notice); + } + // Only allow modification of read only properties when in Rebuild mode + if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && restore_mode != nmos::nc_restore_mode::restore_mode::rebuild) + { + 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::error, U("read only properties can not be modified in Modify restore mode.")); + web::json::push_back(property_restore_notices, property_restore_notice); + } + return property_restore_notices; + } + + web::json::value modify_device_model(nmos::resources& resources, nmos::experimental::control_protocol_state& control_protocol_state, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const web::json::value& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + { + auto object_properties_set_validation_values = web::json::value::array(); + + // filter for the target_role_path and child objects + const auto& filtered_object_properties_holders = boost::copy_range>(object_properties_holders + | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) + { + return is_role_path_root(target_role_path, nmos::fields::nc::path(object_properties_holder)); + }) + ); + web::json::value child_object_properties_holders = web::json::value::array(); + for (const auto& filtered_holder : filtered_object_properties_holders) + { + web::json::push_back(child_object_properties_holders, filtered_holder); + } + + // get object_properties_holder for the target role path, if there is one + const auto& target_object_properties_holders = boost::copy_range>(object_properties_holders + | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) + { + return target_role_path == nmos::fields::nc::path(object_properties_holder); + }) + ); + // there should be 0 or 1 object_properties_holder for any role path. + if (target_object_properties_holders.size() > 1) + { + const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(nmos::fields::nc::path(*target_object_properties_holders.begin()), nmos::nc_restore_validation_status::failed, web::json::value::array(), U("more than one object_properties_holder for role path")); + web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + return object_properties_set_validation_values; + } + + const auto& found = nmos::find_control_protocol_resource_by_role_path(resources, target_role_path); + + if (resources.end() != found) + { + nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)); + + if (nmos::is_nc_block(class_id)) + { + // if rebuildable and the block has changed then callback + if (nmos::fields::nc::is_rebuildable(found->data) && target_object_properties_holders.size() && is_block_modified(*found, *target_object_properties_holders.begin())) + { + // call back to application code + return modify_rebuildable_block(control_protocol_state, get_control_protocol_class_descriptor, target_role_path, child_object_properties_holders, recurse, restore_mode, validate); + } + // iterate through child objects + if (found->data.has_field(nmos::fields::nc::members)) + { + const auto& members = nmos::fields::nc::members(found->data); + + for (const auto& member : members) + { + const auto& child = find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(member)))); + + if (resources.end() != child) + { + auto child_role_path = web::json::value::array(); + for (const auto& path_element : target_role_path.as_array()) + { + web::json::push_back(child_role_path, path_element); + } + web::json::push_back(child_role_path, nmos::fields::nc::role(child->data)); + + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, control_protocol_state, get_control_protocol_class_descriptor, child_role_path, child_object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) + { + web::json::push_back(object_properties_set_validation_values, validation_values); + } + } + } + } + } + for (const auto& target_object_properties_holder : target_object_properties_holders) + { + auto property_restore_notices = web::json::value::array(); + auto property_modify_list = web::json::value::array(); + unsigned int rebuildable_property_count = 0; + // Validate property_values - filter out the incorrect, ignored or unallowed + for (const auto& property_value : nmos::fields::nc::values(target_object_properties_holder)) + { + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_descriptor = nmos::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); + const auto& property_restore_notices_ = check_property_value(property_value, property_descriptor, restore_mode); + if (property_restore_notices_.size() > 0) + { + for (const auto& notice : property_restore_notices_.as_array()) + { + web::json::push_back(property_restore_notices, notice); + } + continue; + } + // Ignore if no change is being requested + if (found->data.at(nmos::fields::nc::name(property_descriptor)) == nmos::fields::nc::value(property_value)) + { + continue; + } + // Only allow modification of read only properties when in Rebuild mode + if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && restore_mode == nmos::nc_restore_mode::restore_mode::rebuild) + { + rebuildable_property_count++; + } + web::json::push_back(property_modify_list, property_value); + } + if (rebuildable_property_count > 0 && property_modify_list.as_array().size() > 0) + { + // If this is a read only property then we should call back to the application code to + // check that it's OK to change this value. Bear in mind that this could be a class Id, or an oid or some other + // property that we don't want changed + const auto& object_properties_set_validation = modify_read_only_config_properties(control_protocol_state, get_control_protocol_class_descriptor, target_role_path, property_modify_list, recurse, restore_mode, validate); + // add in already generated property_restore_notices + auto modified_object_properties_set_validation = object_properties_set_validation; + auto& notices = nmos::fields::nc::notices(modified_object_properties_set_validation); + for (const auto& notice : property_restore_notices.as_array()) + { + web::json::push_back(notices, notice); + } + + web::json::push_back(object_properties_set_validation_values, modified_object_properties_set_validation); + } + else + { + for (const auto& property_value : property_modify_list.as_array()) + { + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + + if (!validate) + { + // modify control protocol resources + const auto& value = nmos::fields::nc::value(property_value); + + modify_control_protocol_resource(resources, found->id, [&](nmos::resource& r_) + { + r_.data[nmos::fields::nc::name(property_value)] = value; + + }, nmos::make_property_changed_event(nmos::fields::nc::oid(found->data), {{property_id, nmos::nc_property_change_type::type::value_changed, value}})); + } + } + const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_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 object_properties_set_validation_values; + } + + 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::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + { + auto object_properties_set_validation_values = web::json::value::array(); + + const auto target_role_path = get_role_path(resources, resource); + + // Detect and warn if there are any object_properties_holders outside of the target role path's scope + const auto& orphan_object_properties_holders = boost::copy_range>(object_properties_holders + | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) + { + return !nmos::is_role_path_root(target_role_path, nmos::fields::nc::path(object_properties_holder)); + }) + ); + for (const auto& orphan_object_properties_holder : orphan_object_properties_holders) + { + const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(nmos::fields::nc::path(orphan_object_properties_holder), nmos::nc_restore_validation_status::not_found, web::json::value::array(), U("object role path not found under target role path")); + web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + } + + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, control_protocol_state, get_control_protocol_class_descriptor, target_role_path, object_properties_holders, recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) + { + web::json::push_back(object_properties_set_validation_values, validation_values); + } + + return object_properties_set_validation_values; + } + + web::json::value validate_set_properties_by_path(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& restore_mode, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + { + // Do something with validation fingerprint? + const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); + + const auto& object_properties_set_validation = apply_backup_data_set(resources, control_protocol_state, get_control_protocol_class_descriptor, resource, object_properties_holders, recurse, restore_mode, true, modify_read_only_config_properties, modify_rebuildable_block); + + return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); + } + + web::json::value set_properties_by_path(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& restore_mode, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + { + // Do something with validation fingerprint? + const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); + + const auto& object_properties_set_validation = apply_backup_data_set(resources, control_protocol_state, get_control_protocol_class_descriptor, resource, object_properties_holders, recurse, restore_mode, false, modify_read_only_config_properties, modify_rebuildable_block); + + return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); + } + +} \ No newline at end of file diff --git a/Development/nmos/configuration_methods.h b/Development/nmos/configuration_methods.h new file mode 100644 index 000000000..1945bc445 --- /dev/null +++ b/Development/nmos/configuration_methods.h @@ -0,0 +1,35 @@ +#ifndef NMOS_CONFIGURATION_METHODS_H +#define NMOS_CONFIGURATION_METHODS_H + +#include "nmos/configuration_handlers.h" +#include "nmos/control_protocol_handlers.h" +#include "nmos/resources.h" + +namespace slog +{ + class base_gate; +} + +namespace nmos +{ + struct control_protocol_resource; + + // Implementation of IS-14 function for creating backup dataset from a Device Model + web::json::value get_properties_by_path(const nmos::resources& resources, 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, bool recurse); + + // Check to see if role_path is sub path of parent_role_path + bool is_role_path_root(const web::json::value& role_path_, const web::json::value& parent_role_path); + + bool is_block_modified(const nmos::resource& resource, const web::json::value& object_properties_holder); + + // Get role path of resource given the Device Model resources + web::json::value get_role_path(const nmos::resources& resources, const nmos::resource& resource); + + 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::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); + + web::json::value validate_set_properties_by_path(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& restore_mode, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); + + web::json::value set_properties_by_path(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& restore_mode, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); +} + +#endif \ No newline at end of file diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index cb43dfb64..72c06883c 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -718,11 +718,11 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock - web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members) + web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members, bool is_rebuildable) { using web::json::value; - auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); + auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints, is_rebuildable); data[nmos::fields::nc::enabled] = value::boolean(enabled); data[nmos::fields::nc::members] = members; diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index b748f4c36..41ad95b5e 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -172,7 +172,7 @@ namespace nmos web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool is_rebuildable=false); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock - web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members); + web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members, bool is_rebuildable); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled); diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index 485379b09..67d68669f 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -9,22 +9,22 @@ namespace nmos namespace details { // create block resource - control_protocol_resource make_block(nmos::nc_oid oid, const web::json::value& owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) + control_protocol_resource make_block(nmos::nc_oid oid, const web::json::value& owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members, bool is_rebuildable) { using web::json::value; - auto data = details::make_nc_block(nc_block_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true, members); + auto data = details::make_nc_block(nc_block_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true, members, is_rebuildable); return{ is12_versions::v1_0, types::nc_block, std::move(data), true }; } } // create block resource - control_protocol_resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) + control_protocol_resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members, bool is_rebuildable) { using web::json::value; - return details::make_block(oid, value(owner), role, user_label, description, touchpoints, runtime_property_constraints, members); + return details::make_block(oid, value(owner), role, user_label, description, touchpoints, runtime_property_constraints, members, is_rebuildable); } // create Root block resource @@ -32,7 +32,7 @@ namespace nmos { using web::json::value; - return details::make_block(nmos::root_block_oid, value::null(), nmos::root_block_role, U("Root"), U("Root block"), value::null(), value::null(), value::array()); + return details::make_block(nmos::root_block_oid, value::null(), nmos::root_block_role, U("Root"), U("Root block"), value::null(), value::null(), value::array(), false); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager diff --git a/Development/nmos/control_protocol_resources.h b/Development/nmos/control_protocol_resources.h index 7e29fa13b..13337d39b 100644 --- a/Development/nmos/control_protocol_resources.h +++ b/Development/nmos/control_protocol_resources.h @@ -14,7 +14,7 @@ namespace nmos struct control_protocol_resource; // create block resource - control_protocol_resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), const web::json::value& members = web::json::value::array()); + control_protocol_resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), const web::json::value& members = web::json::value::array(), bool is_rebuildable=false); // create Root block resource control_protocol_resource make_root_block(); diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 50c494140..cb2b49a5b 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -3,6 +3,7 @@ #include "cpprest/http_utils.h" #include "nmos/control_protocol_methods.h" #include "nmos/control_protocol_resource.h" +#include "nmos/configuration_methods.h" namespace nmos { @@ -201,9 +202,9 @@ namespace nmos return result; }; } - nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(nmos::experimental::control_protocol_state& control_protocol_state, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, validate_set_properties_by_path_handler validate_set_properties_by_path) + nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(nmos::experimental::control_protocol_state& control_protocol_state, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block) { - return [&control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_set_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [&control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, modify_read_only_config_properties, modify_rebuildable_block](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -215,9 +216,9 @@ namespace nmos } auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); - if (validate_set_properties_by_path) + if (modify_read_only_config_properties && modify_rebuildable_block) { - result = validate_set_properties_by_path(control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, data_set, recurse, restore_mode); + result = validate_set_properties_by_path(resources, control_protocol_state, get_control_protocol_class_descriptor, resource, data_set, recurse, restore_mode, modify_read_only_config_properties, modify_rebuildable_block); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -228,9 +229,9 @@ namespace nmos return result; }; } - nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(nmos::experimental::control_protocol_state& control_protocol_state, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, set_properties_by_path_handler set_properties_by_path) + nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(nmos::experimental::control_protocol_state& control_protocol_state, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block) { - return [&control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, set_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [&control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, modify_read_only_config_properties, modify_rebuildable_block](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -241,10 +242,10 @@ namespace nmos return nmos::details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("Null dataSet parameter")); } - auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); - if (set_properties_by_path) + auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("callbacks not implemented")); + if (modify_read_only_config_properties && modify_rebuildable_block) { - result = set_properties_by_path(control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, data_set, recurse, restore_mode); + result = set_properties_by_path(resources, control_protocol_state, get_control_protocol_class_descriptor, resource, data_set, recurse, restore_mode, modify_read_only_config_properties, modify_rebuildable_block); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -257,7 +258,7 @@ namespace nmos } } - control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, get_properties_by_path_handler get_properties_by_path, validate_set_properties_by_path_handler validate_set_properties_by_path, set_properties_by_path_handler set_properties_by_path) + control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, get_properties_by_path_handler get_properties_by_path, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block) { auto to_vector = [](const web::json::value& data) { @@ -396,8 +397,8 @@ namespace nmos to_methods_vector(make_nc_bulk_properties_manager_methods(), { { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(*this, make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), get_properties_by_path)}, - { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(*this, make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), validate_set_properties_by_path) }, - { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(*this, make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), set_properties_by_path) } + { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(*this, make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), modify_read_only_config_properties, modify_rebuildable_block) }, + { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(*this, make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), modify_read_only_config_properties, modify_rebuildable_block) } }), to_vector(make_nc_bulk_properties_manager_events())) } }; diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index ee0904e8b..65b4e8f41 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -3,6 +3,7 @@ #include #include "cpprest/json_utils.h" +#include "nmos/configuration_handlers.h" #include "nmos/control_protocol_handlers.h" #include "nmos/control_protocol_typedefs.h" #include "nmos/mutex.h" @@ -58,7 +59,7 @@ namespace nmos nmos::read_lock read_lock() const { return nmos::read_lock{ mutex }; } nmos::write_lock write_lock() const { return nmos::write_lock{ mutex }; } - control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, get_properties_by_path_handler get_properties_by_path = nullptr, validate_set_properties_by_path_handler validate_set_properties_by_path = nullptr, set_properties_by_path_handler set_properties_by_path = nullptr); + control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, get_properties_by_path_handler get_properties_by_path = nullptr, modify_read_only_config_properties_handler modify_read_only_config_properties = nullptr, modify_rebuildable_block_handler modify_rebuildable_block = nullptr); // insert control class descriptor, false if class descriptor already inserted bool insert(const experimental::control_class_descriptor& control_class_descriptor); // erase control class of the given class id, false if the required class not found diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index 0542ca0eb..16f01e09a 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -124,13 +124,22 @@ namespace nmos }; } + // NcRestoreMode + namespace nc_restore_mode + { + enum restore_mode + { + modify = 0, // Restore mode is Modify + rebuild = 1 // Restore mode is Rebuild + }; + } + // NcRestoreValidationStatus namespace nc_restore_validation_status { enum status { ok = 200, // Restore was successful - excluded = 210, // Excluded from restore due to data provided in the request failed = 400, // Restore failed because relevant backup data set provided is invalid not_found = 404, // Restore failed because the role path is not found in the device model or the device cannot create the role path from the data set device_error = 500 // Restore failed due to an internal device error preventing the restore from happening diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 65f245b70..0dc327f90 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -720,8 +720,7 @@ namespace nmos return resources.end(); } - - resources::const_iterator find_control_protocol_resource_by_role_path(const resources& resources, const utility::string_t& role_path_) + web::json::value parse_role_path(const const utility::string_t& role_path_) { // tokenize the role_path with the '.' delimiter std::list role_path_segments; @@ -733,6 +732,14 @@ namespace nmos { web::json::push_back(role_path, utility::string_t(item.c_str())); } + return role_path; + } + + + resources::const_iterator find_control_protocol_resource_by_role_path(const resources& resources, const utility::string_t& role_path_) + { + const auto& role_path = parse_role_path(role_path_); + return find_control_protocol_resource_by_role_path(resources, role_path); } } diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index 4403a74b4..7b7e85368 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -83,6 +83,9 @@ namespace nmos // find resource based on role path. Roles in role path string must be delimited with a '.' resources::const_iterator find_control_protocol_resource_by_role_path(const resources& resources, const utility::string_t& role_path); + // convert . delimited string into role path object + web::json::value parse_role_path(const utility::string_t& role_path); + // method parameters constraints validation, may throw nmos::control_protocol_exception void method_parameters_contraints_validation(const web::json::value& arguments, const web::json::value& nc_method_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor); } diff --git a/Development/nmos/node_server.h b/Development/nmos/node_server.h index 2dc96e4da..534a1caa0 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -7,6 +7,7 @@ #include "nmos/channelmapping_activation.h" #include "nmos/connection_api.h" #include "nmos/connection_activation.h" +#include "nmos/configuration_handlers.h" #include "nmos/control_protocol_handlers.h" #include "nmos/node_behaviour.h" #include "nmos/node_system_behaviour.h" @@ -27,7 +28,7 @@ namespace nmos // underlying implementation into the server instance for the NMOS Node struct node_implementation { - node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::get_properties_by_path_handler get_properties_by_path, nmos::validate_set_properties_by_path_handler validate_set_properties_by_path, nmos::set_properties_by_path_handler set_properties_by_path) + node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::get_properties_by_path_handler get_properties_by_path, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) : load_server_certificates(std::move(load_server_certificates)) , load_dh_param(std::move(load_dh_param)) , load_ca_certificates(std::move(load_ca_certificates)) @@ -51,8 +52,8 @@ namespace nmos , get_control_protocol_method_descriptor(std::move(get_control_protocol_method_descriptor)) , control_protocol_property_changed(std::move(control_protocol_property_changed)) , get_properties_by_path(std::move(get_properties_by_path)) - , validate_set_properties_by_path(std::move(validate_set_properties_by_path)) - , set_properties_by_path(std::move(set_properties_by_path)) + , modify_read_only_config_properties(std::move(modify_read_only_config_properties)) + , modify_rebuildable_block(std::move(modify_rebuildable_block)) {} // use the default constructor and chaining member functions for fluent initialization @@ -86,8 +87,8 @@ namespace nmos node_implementation& on_get_control_protocol_method_descriptor(nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor) { this->get_control_protocol_method_descriptor = std::move(get_control_protocol_method_descriptor); return *this; } node_implementation& on_control_protocol_property_changed(nmos::control_protocol_property_changed_handler control_protocol_property_changed) { this->control_protocol_property_changed = std::move(control_protocol_property_changed); return *this; } node_implementation& on_get_properties_by_path(nmos::get_properties_by_path_handler get_properties_by_path) { this->get_properties_by_path = std::move(get_properties_by_path); return *this; } - node_implementation& on_validate_set_properties_by_path(nmos::validate_set_properties_by_path_handler validate_set_properties_by_path) { this->validate_set_properties_by_path = std::move(validate_set_properties_by_path); return *this; } - node_implementation& on_set_properties_by_path(nmos::set_properties_by_path_handler set_properties_by_path) { this->set_properties_by_path = std::move(set_properties_by_path); return *this; } + node_implementation& on_modify_read_only_config_properties(nmos::modify_read_only_config_properties_handler modify_read_only_config_properties) { this->modify_read_only_config_properties = std::move(modify_read_only_config_properties); return *this; } + node_implementation& on_modify_rebuildable_block(nmos::modify_rebuildable_block_handler modify_rebuildable_block) { this->modify_rebuildable_block = std::move(modify_rebuildable_block); return *this; } // deprecated, use on_validate_connection_resource_patch node_implementation& on_validate_merged(nmos::details::connection_resource_patch_validator validate_merged) { return on_validate_connection_resource_patch(std::move(validate_merged)); } @@ -133,8 +134,8 @@ namespace nmos // Device Configuration method handlers nmos::get_properties_by_path_handler get_properties_by_path; - nmos::validate_set_properties_by_path_handler validate_set_properties_by_path; - nmos::set_properties_by_path_handler set_properties_by_path; + nmos::modify_read_only_config_properties_handler modify_read_only_config_properties; + nmos::modify_rebuildable_block_handler modify_rebuildable_block; }; // Construct a server instance for an NMOS Node, implementing the IS-04 Node API, IS-05 Connection API, IS-07 Events API diff --git a/Development/nmos/test/configuration_methods_test.cpp b/Development/nmos/test/configuration_methods_test.cpp new file mode 100644 index 000000000..df7c5e06b --- /dev/null +++ b/Development/nmos/test/configuration_methods_test.cpp @@ -0,0 +1,518 @@ +// The first "test" is of course whether the header compiles standalone +#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_resources.h" +#include "nmos/control_protocol_state.h" +#include "nmos/control_protocol_typedefs.h" +#include "nmos/control_protocol_utils.h" +#include "nmos/configuration_handlers.h" +#include "nmos/configuration_methods.h" + +#include "bst/test/test.h" + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testIsRolePathRoot) +{ + { + web::json::value role_path = web::json::value_of({ U("root"), U("path1")}); + web::json::value role_path_root = web::json::value_of({ U("root"), U("path1")}); + + BST_REQUIRE(nmos::is_role_path_root(role_path_root, role_path)); + } + { + web::json::value role_path = web::json::value_of({ U("root"), U("path1"), U("path2"), U("path3")}); + web::json::value role_path_root = web::json::value_of({ U("root"), U("path1"), U("path2") }); + + BST_REQUIRE(nmos::is_role_path_root(role_path_root, role_path)); + } + { + web::json::value role_path = web::json::value_of({ U("root"), U("path1")}); + web::json::value role_path_root = web::json::value_of({ U("root"), U("path1"), U("path2") }); + + BST_REQUIRE(!nmos::is_role_path_root(role_path_root, role_path)); + } + { + web::json::value role_path = web::json::value_of({ U("root"), U("path3"), U("path4") }); + web::json::value role_path_root = web::json::value_of({ U("root"), U("path1"), U("path2") }); + + BST_REQUIRE(!nmos::is_role_path_root(role_path_root, role_path)); + } + { + web::json::value role_path = web::json::value_of({ U("path3"), U("path4") }); + web::json::value role_path_root = web::json::value_of({ U("root"), U("path1"), U("path2") }); + + BST_REQUIRE(!nmos::is_role_path_root(role_path_root, role_path)); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testIsBlockModified) +{ + using web::json::value_of; + using web::json::value; + + // Create Device Model + // root + auto root_block = nmos::make_root_block(); + nmos::nc_oid oid = nmos::root_block_oid; + // root, receivers + auto receivers = nmos::make_block(++oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); + nmos::nc_oid receiver_block_oid = oid; + // root, receivers, mon1 + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + nmos::nc_oid monitor_1_oid = oid; + // root, receivers, mon2 + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + nmos::nc_oid monitor_2_oid = oid; + nmos::push_back(receivers, monitor1); + // add example-control to root-block + nmos::push_back(receivers, monitor2); + // add stereo-gain to root-block + nmos::push_back(root_block, receivers); + + // Create Object Properties Holder + value role_path = value::array(); + push_back(role_path, U("root")); + push_back(role_path, U("receivers")); + + // Members unchanged + { + value property_value_holders = value::array(); + + value members = value::array(); + nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + { + value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + { + value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + nmos::nc_property_id property_id(2, 2); // block members + web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_value_holders, property_value_holder); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + + BST_CHECK(!nmos::is_block_modified(receivers, object_properties_holder)); + } + + // Changed number of members + { + value property_value_holders = value::array(); + + value members = value::array(); + nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + value block_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, class_id, U("label"), receiver_block_oid); + push_back(members, block_descriptor); + nmos::nc_property_id property_id(2, 2); // block members + web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_value_holders, property_value_holder); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + + BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); + } + + // Changed oids + { + value property_value_holders = value::array(); + + value members = value::array(); + nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + { + value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), 10, true, class_id, U("monitor 1"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + { + value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), 20, true, class_id, U("monitor 2"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + nmos::nc_property_id property_id(2, 2); // block members + web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_value_holders, property_value_holder); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + + BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); + } + + // Changed roles + { + value property_value_holders = value::array(); + + value members = value::array(); + nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + { + value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon3"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + { + value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon4"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + nmos::nc_property_id property_id(2, 2); // block members + web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_value_holders, property_value_holder); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + + BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); + } + + // Changed class id + { + value property_value_holders = value::array(); + + value members = value::array(); + nmos::nc_class_id class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 1 }); + { + value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + { + value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + nmos::nc_property_id property_id(2, 2); // block members + web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_value_holders, property_value_holder); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + + BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); + } + + // Changed owner oid + { + value property_value_holders = value::array(); + + value members = value::array(); + nmos::nc_class_id class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 1 }); + { + value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), 20); + push_back(members, block_member_descriptor); + } + { + value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), 20); + push_back(members, block_member_descriptor); + } + nmos::nc_property_id property_id(2, 2); // block members + web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_value_holders, property_value_holder); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + + BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); + } + + // Changed constant oid + { + value property_value_holders = value::array(); + + value members = value::array(); + nmos::nc_class_id class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 1 }); + { + value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, false, class_id, U("monitor 1"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + { + value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, false, class_id, U("monitor 2"), receiver_block_oid); + push_back(members, block_member_descriptor); + } + nmos::nc_property_id property_id(2, 2); // block members + web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_value_holders, property_value_holder); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + + BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); + } + + +} + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testApplyBackupDataSet) +{ + using web::json::value_of; + using web::json::value; + + nmos::resources resources; + nmos::experimental::control_protocol_state control_protocol_state; + nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor = nmos::make_get_control_protocol_class_descriptor_handler(control_protocol_state); + + // Create Device Model + // root + auto root_block = nmos::make_root_block(); + nmos::nc_oid oid = nmos::root_block_oid; + // root, Class Date: Wed, 18 Dec 2024 17:07:08 +0000 Subject: [PATCH 135/250] Notify model of changes --- Development/nmos/configuration_api.cpp | 4 +++- Development/nmos/configuration_methods.cpp | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 707d70c98..81610d565 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -555,6 +555,7 @@ namespace nmos auto status = nmos::fields::nc::status(result); auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; set_reply(res, code, result); + model.notify(); } } else @@ -718,7 +719,7 @@ namespace nmos const auto& bulk_properties_manager = find_control_protocol_resource_by_role_path(resources, nmos::bulk_properties_manager_role); if (resources.end() != resource && resources.end() != bulk_properties_manager) { - return details::extract_json(req, gate_).then([res, resources, resource, get_control_protocol_method_descriptor, version, &gate_](value body) mutable + return details::extract_json(req, gate_).then([res, &resources, resource, get_control_protocol_method_descriptor, version, &model, &gate_](value body) mutable { // Validate JSON syntax according to the schema details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_set_request_schema_uri(version)); @@ -739,6 +740,7 @@ namespace nmos else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } else { code = status_codes::InternalError; } + model.notify(); } catch (const nmos::control_protocol_exception& e) { diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index d69406cbd..f37c960f6 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -388,9 +388,9 @@ namespace nmos // modify control protocol resources const auto& value = nmos::fields::nc::value(property_value); - modify_control_protocol_resource(resources, found->id, [&](nmos::resource& r_) + modify_control_protocol_resource(resources, found->id, [&](nmos::resource& resource_) { - r_.data[nmos::fields::nc::name(property_value)] = value; + resource_.data[nmos::fields::nc::name(property_value)] = value; }, nmos::make_property_changed_event(nmos::fields::nc::oid(found->data), {{property_id, nmos::nc_property_change_type::type::value_changed, value}})); } From eb81cc8a8dc198017b2d9e4ba64b2adb261a9fb5 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 18 Dec 2024 17:41:35 +0000 Subject: [PATCH 136/250] Remove repeated qualifier --- Development/nmos/control_protocol_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 0dc327f90..de09876ed 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -720,7 +720,7 @@ namespace nmos return resources.end(); } - web::json::value parse_role_path(const const utility::string_t& role_path_) + web::json::value parse_role_path(const utility::string_t& role_path_) { // tokenize the role_path with the '.' delimiter std::list role_path_segments; From f51e31101152c50584714af2112b95431ae2b793 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 18 Dec 2024 17:41:48 +0000 Subject: [PATCH 137/250] Remove spurious ; --- Development/nmos/control_protocol_typedefs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index 16f01e09a..e2d975750 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -154,7 +154,7 @@ namespace nmos warning = 300, // Warning property restore notice error = 400 // Error property restore notice }; - }; + } // NcElementId // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncelementid From fde3434fcc30a9bed76653dd01c64e77cfd2df92 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 18 Dec 2024 17:42:02 +0000 Subject: [PATCH 138/250] Type wrangling --- Development/nmos/configuration_methods.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index f37c960f6..6cec850dd 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -145,7 +145,7 @@ namespace nmos // root can't be longed that the path return false; } - for (int i = 0; i < role_path_root.as_array().size(); ++i) + for (size_t i = 0; i < role_path_root.as_array().size(); ++i) { if (role_path_root.as_array().at(i) != role_path_.as_array().at(i)) { @@ -221,7 +221,10 @@ namespace nmos size_t validation_fingerprint = details::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); + utility::ostringstream_t ss; + ss << validation_fingerprint; + + auto bulk_values_holder = nmos::details::make_nc_bulk_values_holder(ss.str(), object_properties_holders); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, bulk_values_holder); } From 701494b88b133b567d3e833ab65ba516739a9c60 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 18 Dec 2024 19:56:26 +0000 Subject: [PATCH 139/250] std::list? --- Development/nmos/control_protocol_utils.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index de09876ed..cd4aae94b 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -6,6 +6,7 @@ #include #include "bst/regex.h" #include "cpprest/json_utils.h" +#include #include "nmos/control_protocol_resource.h" #include "nmos/control_protocol_state.h" #include "nmos/json_fields.h" From f27ffe1dded10d4f1ae43b397eb742ce8ac3547e Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Thu, 19 Dec 2024 17:44:54 +0000 Subject: [PATCH 140/250] Remove application code defined get_properties_by_path handler. Call get_properties_by_path directly in configuration api --- Development/nmos-cpp-node/main.cpp | 2 +- .../nmos-cpp-node/node_implementation.cpp | 16 +---- Development/nmos/configuration_api.cpp | 59 ++++++------------- Development/nmos/configuration_methods.cpp | 4 +- Development/nmos/configuration_methods.h | 2 +- Development/nmos/control_protocol_state.cpp | 23 ++------ Development/nmos/control_protocol_state.h | 2 +- Development/nmos/control_protocol_utils.cpp | 2 +- Development/nmos/node_server.h | 7 +-- 9 files changed, 33 insertions(+), 84 deletions(-) diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index 290ca17c6..d9bf0a6c5 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -138,7 +138,7 @@ int main(int argc, char* argv[]) .on_request_authorization_code(nmos::experimental::make_request_authorization_code_handler(gate)); // may be omitted, only required for OAuth client which is using the Authorization Code Flow to obtain the access token } - nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.get_properties_by_path, node_implementation.modify_read_only_config_properties, node_implementation.modify_rebuildable_block); + nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.modify_read_only_config_properties, node_implementation.modify_rebuildable_block); if (0 <= nmos::fields::control_protocol_ws_port(node_model.settings)) { node_implementation diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 3ea68451d..5a9874123 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1722,17 +1722,6 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control }; } -// 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) -{ - 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, bool recurse) - { - slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do get_properties_by_path"; - - return nmos::get_properties_by_path(resources, control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, recurse); - }; -} - // Example Device Configuration callback for validating a back-up dataset nmos::modify_read_only_config_properties_handler make_modify_read_only_config_properties_handler(nmos::resources& resources, slog::base_gate& gate) { @@ -1910,7 +1899,6 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode .on_validate_channelmapping_output_map(make_node_implementation_map_validator()) // may be omitted if not required .on_channelmapping_activated(make_node_implementation_channelmapping_activation_handler(gate)) .on_control_protocol_property_changed(make_node_implementation_control_protocol_property_changed_handler(gate)) // may be omitted if IS-12 not required - .on_get_properties_by_path(make_node_implementation_get_properties_by_path_handler(model.control_protocol_resources, gate)) // may be omitted if IS-14 not required - .on_modify_read_only_config_properties(make_modify_read_only_config_properties_handler(model.control_protocol_resources, gate)) // may be omitted if IS-14 not required - .on_modify_rebuildable_block(make_modify_rebuildable_block_handler(model.control_protocol_resources, gate)); // may be omitted if IS-14 not required + .on_modify_read_only_config_properties(make_modify_read_only_config_properties_handler(model.control_protocol_resources, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required + .on_modify_rebuildable_block(make_modify_rebuildable_block_handler(model.control_protocol_resources, gate)); // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required } diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 81610d565..044c09fb7 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -4,6 +4,7 @@ #include #include "cpprest/json_validator.h" #include "nmos/api_utils.h" +#include "nmos/configuration_methods.h" #include "nmos/control_protocol_handlers.h" #include "nmos/control_protocol_methods.h" #include "nmos/control_protocol_resource.h" @@ -568,68 +569,46 @@ namespace nmos }); }); - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::GET, [&model, get_control_protocol_method_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::GET, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); - const auto& bulk_properties_manager = find_control_protocol_resource_by_role_path(resources, nmos::bulk_properties_manager_role); - if (resources.end() != resource && resources.end() != bulk_properties_manager) + if (resources.end() != resource) { - auto method = get_control_protocol_method_descriptor(nc_bulk_properties_manager_class_id, nc_bulk_properties_manager_get_properties_by_path_method_id); - auto& nc_method_descriptor = method.first; - auto& control_method_handler = method.second; web::http::status_code code{ status_codes::BadRequest }; value method_result; - if (control_method_handler) + try { - try - { - bool recurse = details::parse_recurse_query_parameter(req.request_uri().query()); + bool recurse = details::parse_recurse_query_parameter(req.request_uri().query()); - method_result = control_method_handler(resources, *resource, value_of({ { nmos::fields::nc::recurse, recurse } }), nmos::fields::nc::is_deprecated(nc_method_descriptor), gate_); + method_result = get_properties_by_path(resources, *resource, recurse, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); - auto status = nmos::fields::nc::status(method_result); - if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } - else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } - else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } - else { code = status_codes::InternalError; } - } - catch (const nmos::control_protocol_exception& e) - { - // invalid arguments - utility::stringstream_t ss; - ss << U("parameter error: ") << e.what(); - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); - - code = status_codes::BadRequest; - } + auto status = nmos::fields::nc::status(method_result); + if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } + else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } + else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } + else { code = status_codes::InternalError; } } - else + catch (const nmos::control_protocol_exception& e) { - // unknown methodId - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("get_properties_by_path unsupported by bulk properties manager.")); + // invalid arguments + utility::stringstream_t ss; + ss << U("parameter error: ") << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); - code = status_codes::NotFound; + code = status_codes::BadRequest; } set_reply(res, code, method_result); } else { - if (resources.end() == bulk_properties_manager) - { - // no bulk properties manager - set_error_reply(res, status_codes::NotFound, U("Bulk Properties Manager not found at ") + nmos::bulk_properties_manager_role); - } - else - { - // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); - } + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); } return pplx::task_from_result(true); diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index 6cec850dd..6c154a97e 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -208,13 +208,11 @@ namespace nmos return false; } - web::json::value get_properties_by_path(const nmos::resources& resources, 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, bool recurse) + web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { using web::json::value; using web::json::value_of; - auto lock = control_protocol_state.read_lock(); - value object_properties_holders = value::array(); details::populate_object_property_holder(resources, get_control_protocol_class_descriptor, resource, recurse, object_properties_holders); diff --git a/Development/nmos/configuration_methods.h b/Development/nmos/configuration_methods.h index 1945bc445..5637937a9 100644 --- a/Development/nmos/configuration_methods.h +++ b/Development/nmos/configuration_methods.h @@ -15,7 +15,7 @@ namespace nmos struct control_protocol_resource; // Implementation of IS-14 function for creating backup dataset from a Device Model - web::json::value get_properties_by_path(const nmos::resources& resources, 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, bool recurse); + web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor); // Check to see if role_path is sub path of parent_role_path bool is_role_path_root(const web::json::value& role_path_, const web::json::value& parent_role_path); diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index cb2b49a5b..898aa5cb5 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -180,26 +180,13 @@ namespace nmos return get_datatype(arguments, is_deprecated, get_control_protocol_datatype_descriptor, gate); }; } - nmos::experimental::control_protocol_method_handler make_nc_get_properties_by_path_handler(nmos::experimental::control_protocol_state& control_protocol_state, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_properties_by_path_handler get_properties_by_path) + nmos::experimental::control_protocol_method_handler make_nc_get_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { - return [&control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_properties_by_path](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); - // Delegate to user defined handler - - auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); - if (get_properties_by_path) - { - result = get_properties_by_path(control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, resource, recurse); - - const auto& status = nmos::fields::nc::status(result); - if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) - { - return nmos::details::make_nc_method_result({ nmos::nc_method_status::method_deprecated }, nmos::fields::nc::value(result)); - } - } - return result; + return nmos::get_properties_by_path(resources, resource, recurse, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); }; } nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(nmos::experimental::control_protocol_state& control_protocol_state, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block) @@ -258,7 +245,7 @@ namespace nmos } } - control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, get_properties_by_path_handler get_properties_by_path, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block) + control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block) { auto to_vector = [](const web::json::value& data) { @@ -396,7 +383,7 @@ namespace nmos to_vector(make_nc_bulk_properties_manager_properties()), to_methods_vector(make_nc_bulk_properties_manager_methods(), { - { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(*this, make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), get_properties_by_path)}, + { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this))}, { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(*this, make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), modify_read_only_config_properties, modify_rebuildable_block) }, { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(*this, make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), modify_read_only_config_properties, modify_rebuildable_block) } }), diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index 65b4e8f41..d89ac61a2 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -59,7 +59,7 @@ namespace nmos nmos::read_lock read_lock() const { return nmos::read_lock{ mutex }; } nmos::write_lock write_lock() const { return nmos::write_lock{ mutex }; } - control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, get_properties_by_path_handler get_properties_by_path = nullptr, modify_read_only_config_properties_handler modify_read_only_config_properties = nullptr, modify_rebuildable_block_handler modify_rebuildable_block = nullptr); + control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, modify_read_only_config_properties_handler modify_read_only_config_properties = nullptr, modify_rebuildable_block_handler modify_rebuildable_block = nullptr); // insert control class descriptor, false if class descriptor already inserted bool insert(const experimental::control_class_descriptor& control_class_descriptor); // erase control class of the given class id, false if the required class not found diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index cd4aae94b..e18e89ff7 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -1,12 +1,12 @@ #include "nmos/control_protocol_utils.h" +#include #include #include #include #include #include "bst/regex.h" #include "cpprest/json_utils.h" -#include #include "nmos/control_protocol_resource.h" #include "nmos/control_protocol_state.h" #include "nmos/json_fields.h" diff --git a/Development/nmos/node_server.h b/Development/nmos/node_server.h index 534a1caa0..1c48b25a0 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -28,7 +28,7 @@ namespace nmos // underlying implementation into the server instance for the NMOS Node struct node_implementation { - node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::get_properties_by_path_handler get_properties_by_path, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) : load_server_certificates(std::move(load_server_certificates)) , load_dh_param(std::move(load_dh_param)) , load_ca_certificates(std::move(load_ca_certificates)) @@ -51,7 +51,6 @@ namespace nmos , get_control_protocol_datatype_descriptor(std::move(get_control_protocol_datatype_descriptor)) , get_control_protocol_method_descriptor(std::move(get_control_protocol_method_descriptor)) , control_protocol_property_changed(std::move(control_protocol_property_changed)) - , get_properties_by_path(std::move(get_properties_by_path)) , modify_read_only_config_properties(std::move(modify_read_only_config_properties)) , modify_rebuildable_block(std::move(modify_rebuildable_block)) {} @@ -86,7 +85,6 @@ namespace nmos node_implementation& on_get_control_datatype_descriptor(nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { this->get_control_protocol_datatype_descriptor = std::move(get_control_protocol_datatype_descriptor); return *this; } node_implementation& on_get_control_protocol_method_descriptor(nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor) { this->get_control_protocol_method_descriptor = std::move(get_control_protocol_method_descriptor); return *this; } node_implementation& on_control_protocol_property_changed(nmos::control_protocol_property_changed_handler control_protocol_property_changed) { this->control_protocol_property_changed = std::move(control_protocol_property_changed); return *this; } - node_implementation& on_get_properties_by_path(nmos::get_properties_by_path_handler get_properties_by_path) { this->get_properties_by_path = std::move(get_properties_by_path); return *this; } node_implementation& on_modify_read_only_config_properties(nmos::modify_read_only_config_properties_handler modify_read_only_config_properties) { this->modify_read_only_config_properties = std::move(modify_read_only_config_properties); return *this; } node_implementation& on_modify_rebuildable_block(nmos::modify_rebuildable_block_handler modify_rebuildable_block) { this->modify_rebuildable_block = std::move(modify_rebuildable_block); return *this; } @@ -132,8 +130,7 @@ namespace nmos nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor; nmos::control_protocol_property_changed_handler control_protocol_property_changed; - // Device Configuration method handlers - nmos::get_properties_by_path_handler get_properties_by_path; + // Device Configuration handlers nmos::modify_read_only_config_properties_handler modify_read_only_config_properties; nmos::modify_rebuildable_block_handler modify_rebuildable_block; }; From 751f28866289aa985ce97a4d16a880ed96353383 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 20 Dec 2024 10:15:00 +0000 Subject: [PATCH 141/250] Remove redundant handlers --- Development/nmos/control_protocol_handlers.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index f487b0d83..0dcd84787 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -36,12 +36,6 @@ namespace nmos // this callback should not throw exceptions, as the relevant property will already has been changed and those changes will not be rolled back typedef std::function control_protocol_property_changed_handler; - // Device Configuration handlers - // these callbacks should not throw exceptions - typedef std::function get_properties_by_path_handler; - typedef std::function validate_set_properties_by_path_handler; - typedef std::function set_properties_by_path_handler; - namespace experimental { // control method handler definition From c3e300297085f150847c657bc130d0d87933f61d Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 20 Dec 2024 10:15:20 +0000 Subject: [PATCH 142/250] Add comments to endpoints --- Development/nmos/configuration_api.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 044c09fb7..9e78aac6b 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -164,6 +164,7 @@ namespace nmos return pplx::task_from_result(true); }); + // GET /rolePaths configuration_api.support(U("/rolePaths/?"), methods::GET, [&model](http_request req, http_response res, const string_t&, const route_parameters&) { auto lock = model.read_lock(); @@ -187,6 +188,7 @@ namespace nmos return pplx::task_from_result(true); }); + // GET /rolePaths/{rolePath} configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/?"), methods::GET, [&model, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); @@ -208,6 +210,7 @@ namespace nmos return pplx::task_from_result(true); }); + // GET /rolePaths/{rolePath}/properties configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); @@ -247,6 +250,7 @@ namespace nmos return pplx::task_from_result(true); }); + // GET /rolePaths/{rolePath}/methods configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/methods/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); @@ -293,6 +297,7 @@ namespace nmos return pplx::task_from_result(true); }); + // GET /rolePaths/{rolePath}/descriptor configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/descriptor/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); @@ -348,6 +353,7 @@ namespace nmos return pplx::task_from_result(true); }); + // GET /rolePaths/{rolePath}/properties/{propertyId} configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto property_id = parameters.at(nmos::patterns::propertyId.name); @@ -379,6 +385,7 @@ namespace nmos return pplx::task_from_result(true); }); + // GET /rolePaths/{rolePath}/properties/{propertyId}/descriptor configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/descriptor/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto property_id = parameters.at(nmos::patterns::propertyId.name); @@ -411,6 +418,7 @@ namespace nmos return pplx::task_from_result(true); }); + // GET /rolePaths/{rolePath}/properties/{propertyId}/value configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/value/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto property_id = parameters.at(nmos::patterns::propertyId.name); @@ -443,6 +451,7 @@ namespace nmos return pplx::task_from_result(true); }); + // GET /rolePaths/{rolePath}/methods/{methodId} configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/methods/") + nmos::patterns::methodId.pattern + U("/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, property_changed, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { nmos::api_gate gate(gate_, req, parameters); @@ -518,6 +527,7 @@ namespace nmos }); }); + // PUT /rolePaths/{rolePath}/properties/{propertyId}/value configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/value/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { nmos::api_gate gate(gate_, req, parameters); @@ -569,6 +579,7 @@ namespace nmos }); }); + // GET /rolePaths/{rolePath}/bulkProperties configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::GET, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); @@ -614,6 +625,7 @@ namespace nmos return pplx::task_from_result(true); }); + // PATCH /rolePaths/{rolePath}/bulkProperties configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_method_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); @@ -687,6 +699,7 @@ namespace nmos return pplx::task_from_result(true); }); + // PUT /rolePaths/{rolePath}/bulkProperties configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_method_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); From 284dcccc8eb4f19b2b5746629ad2cca46a9d4b65 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 20 Dec 2024 10:15:44 +0000 Subject: [PATCH 143/250] Don't pass control_protocol_state to configuration functions --- .../nmos-cpp-node/node_implementation.cpp | 4 ++-- Development/nmos/configuration_handlers.h | 4 ++-- Development/nmos/configuration_methods.cpp | 20 +++++++++---------- Development/nmos/configuration_methods.h | 6 +++--- Development/nmos/control_protocol_state.cpp | 16 +++++++-------- .../nmos/test/configuration_methods_test.cpp | 16 +++++++-------- 6 files changed, 33 insertions(+), 33 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 5a9874123..6c5897b9a 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1725,7 +1725,7 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control // Example Device Configuration callback for validating a back-up dataset nmos::modify_read_only_config_properties_handler make_modify_read_only_config_properties_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, const web::json::value& target_role_path, const web::json::value& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate) + return [&resources, &gate](nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const web::json::value& target_role_path, const web::json::value& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate) { slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do modify_read_only_config_properties"; @@ -1736,7 +1736,7 @@ nmos::modify_read_only_config_properties_handler make_modify_read_only_config_pr // Example Device Configuration callback for restoring a back-up dataset nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_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, const web::json::value& target_role_path, const web::json::value& property_values, bool recurse, const web::json::value& restore_mode, bool validate) + return [&resources, &gate](nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const web::json::value& target_role_path, const web::json::value& property_values, bool recurse, const web::json::value& restore_mode, bool validate) { slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do modify_rebuildable_block"; diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h index f6d4258cc..271664136 100644 --- a/Development/nmos/configuration_handlers.h +++ b/Development/nmos/configuration_handlers.h @@ -19,12 +19,12 @@ namespace nmos } // This callback is invoked if attempting to modify read only properties when restoring a configuration. // This function should modify the Device Model object directly and return a corresponding NcObjectPropertiesSetValidation object - typedef std::function modify_read_only_config_properties_handler; + typedef std::function modify_read_only_config_properties_handler; // This callback is invoked if attempting to modify a rebuildable block when restoring a configuration. // This function should handle the modification of the Device Model and any corresponding NMOS resources // and return correpsonding NcObjectPropertiesSetValidation objects for each object modified/added - typedef std::function modify_rebuildable_block_handler; + typedef std::function modify_rebuildable_block_handler; } #endif diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index 6c154a97e..22120b329 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -258,7 +258,7 @@ namespace nmos return property_restore_notices; } - web::json::value modify_device_model(nmos::resources& resources, nmos::experimental::control_protocol_state& control_protocol_state, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const web::json::value& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value modify_device_model(nmos::resources& resources, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const web::json::value& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) { auto object_properties_set_validation_values = web::json::value::array(); @@ -302,7 +302,7 @@ namespace nmos if (nmos::fields::nc::is_rebuildable(found->data) && target_object_properties_holders.size() && is_block_modified(*found, *target_object_properties_holders.begin())) { // call back to application code - return modify_rebuildable_block(control_protocol_state, get_control_protocol_class_descriptor, target_role_path, child_object_properties_holders, recurse, restore_mode, validate); + return modify_rebuildable_block(get_control_protocol_class_descriptor, target_role_path, child_object_properties_holders, recurse, restore_mode, validate); } // iterate through child objects if (found->data.has_field(nmos::fields::nc::members)) @@ -322,7 +322,7 @@ namespace nmos } web::json::push_back(child_role_path, nmos::fields::nc::role(child->data)); - web::json::value child_object_properties_set_validation_values = modify_device_model(resources, control_protocol_state, get_control_protocol_class_descriptor, child_role_path, child_object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, get_control_protocol_class_descriptor, child_role_path, child_object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) { web::json::push_back(object_properties_set_validation_values, validation_values); @@ -367,7 +367,7 @@ namespace nmos // If this is a read only property then we should call back to the application code to // check that it's OK to change this value. Bear in mind that this could be a class Id, or an oid or some other // property that we don't want changed - const auto& object_properties_set_validation = modify_read_only_config_properties(control_protocol_state, get_control_protocol_class_descriptor, target_role_path, property_modify_list, recurse, restore_mode, validate); + const auto& object_properties_set_validation = modify_read_only_config_properties(get_control_protocol_class_descriptor, target_role_path, property_modify_list, recurse, restore_mode, validate); // add in already generated property_restore_notices auto modified_object_properties_set_validation = object_properties_set_validation; auto& notices = nmos::fields::nc::notices(modified_object_properties_set_validation); @@ -404,7 +404,7 @@ namespace nmos return object_properties_set_validation_values; } - 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::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value apply_backup_data_set(nmos::resources& resources, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) { auto object_properties_set_validation_values = web::json::value::array(); @@ -423,7 +423,7 @@ namespace nmos web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); } - web::json::value child_object_properties_set_validation_values = modify_device_model(resources, control_protocol_state, get_control_protocol_class_descriptor, target_role_path, object_properties_holders, recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, get_control_protocol_class_descriptor, target_role_path, object_properties_holders, recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) { web::json::push_back(object_properties_set_validation_values, validation_values); @@ -432,22 +432,22 @@ namespace nmos return object_properties_set_validation_values; } - web::json::value validate_set_properties_by_path(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& restore_mode, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value validate_set_properties_by_path(nmos::resources& resources, 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& restore_mode, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) { // Do something with validation fingerprint? const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto& object_properties_set_validation = apply_backup_data_set(resources, control_protocol_state, get_control_protocol_class_descriptor, resource, object_properties_holders, recurse, restore_mode, true, modify_read_only_config_properties, modify_rebuildable_block); + const auto& object_properties_set_validation = apply_backup_data_set(resources, get_control_protocol_class_descriptor, resource, object_properties_holders, recurse, restore_mode, true, modify_read_only_config_properties, modify_rebuildable_block); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } - web::json::value set_properties_by_path(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& restore_mode, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value set_properties_by_path(nmos::resources& resources, 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& restore_mode, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) { // Do something with validation fingerprint? const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto& object_properties_set_validation = apply_backup_data_set(resources, control_protocol_state, get_control_protocol_class_descriptor, resource, object_properties_holders, recurse, restore_mode, false, modify_read_only_config_properties, modify_rebuildable_block); + const auto& object_properties_set_validation = apply_backup_data_set(resources, get_control_protocol_class_descriptor, resource, object_properties_holders, recurse, restore_mode, false, modify_read_only_config_properties, modify_rebuildable_block); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } diff --git a/Development/nmos/configuration_methods.h b/Development/nmos/configuration_methods.h index 5637937a9..e3a1c128d 100644 --- a/Development/nmos/configuration_methods.h +++ b/Development/nmos/configuration_methods.h @@ -25,11 +25,11 @@ namespace nmos // Get role path of resource given the Device Model resources web::json::value get_role_path(const nmos::resources& resources, const nmos::resource& resource); - 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::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); + web::json::value apply_backup_data_set(nmos::resources& resources, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); - web::json::value validate_set_properties_by_path(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& restore_mode, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); + web::json::value validate_set_properties_by_path(nmos::resources& resources, 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& restore_mode, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); - web::json::value set_properties_by_path(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& restore_mode, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); + web::json::value set_properties_by_path(nmos::resources& resources, 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& restore_mode, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); } #endif \ No newline at end of file diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 898aa5cb5..47313a7f6 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -189,9 +189,9 @@ namespace nmos return nmos::get_properties_by_path(resources, resource, recurse, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); }; } - nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(nmos::experimental::control_protocol_state& control_protocol_state, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block) + nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block) { - return [&control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, modify_read_only_config_properties, modify_rebuildable_block](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [&get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, modify_read_only_config_properties, modify_rebuildable_block](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -205,7 +205,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); if (modify_read_only_config_properties && modify_rebuildable_block) { - result = validate_set_properties_by_path(resources, control_protocol_state, get_control_protocol_class_descriptor, resource, data_set, recurse, restore_mode, modify_read_only_config_properties, modify_rebuildable_block); + result = validate_set_properties_by_path(resources, get_control_protocol_class_descriptor, resource, data_set, recurse, restore_mode, modify_read_only_config_properties, modify_rebuildable_block); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -216,9 +216,9 @@ namespace nmos return result; }; } - nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(nmos::experimental::control_protocol_state& control_protocol_state, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block) + nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block) { - return [&control_protocol_state, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, modify_read_only_config_properties, modify_rebuildable_block](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, modify_read_only_config_properties, modify_rebuildable_block](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -232,7 +232,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("callbacks not implemented")); if (modify_read_only_config_properties && modify_rebuildable_block) { - result = set_properties_by_path(resources, control_protocol_state, get_control_protocol_class_descriptor, resource, data_set, recurse, restore_mode, modify_read_only_config_properties, modify_rebuildable_block); + result = set_properties_by_path(resources, get_control_protocol_class_descriptor, resource, data_set, recurse, restore_mode, modify_read_only_config_properties, modify_rebuildable_block); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -384,8 +384,8 @@ namespace nmos to_methods_vector(make_nc_bulk_properties_manager_methods(), { { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this))}, - { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(*this, make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), modify_read_only_config_properties, modify_rebuildable_block) }, - { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(*this, make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), modify_read_only_config_properties, modify_rebuildable_block) } + { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), modify_read_only_config_properties, modify_rebuildable_block) }, + { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), modify_read_only_config_properties, modify_rebuildable_block) } }), to_vector(make_nc_bulk_properties_manager_events())) } }; diff --git a/Development/nmos/test/configuration_methods_test.cpp b/Development/nmos/test/configuration_methods_test.cpp index df7c5e06b..fd8df57f5 100644 --- a/Development/nmos/test/configuration_methods_test.cpp +++ b/Development/nmos/test/configuration_methods_test.cpp @@ -267,12 +267,12 @@ BST_TEST_CASE(testApplyBackupDataSet) bool modify_rebuildable_block_called = false; // callback stubs - nmos::modify_read_only_config_properties_handler modify_read_only_config_properties = [&](nmos::experimental::control_protocol_state& control_protocol_state, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const web::json::value& target_role_path, const web::json::value& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate) + nmos::modify_read_only_config_properties_handler modify_read_only_config_properties = [&](nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const web::json::value& target_role_path, const web::json::value& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate) { modify_read_only_config_properties_called = true; return nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, value::array(), U("OK")); }; - nmos::modify_rebuildable_block_handler modify_rebuildable_block = [&](nmos::experimental::control_protocol_state& control_protocol_state, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const web::json::value& target_role_path, const web::json::value& property_values, bool recurse, const web::json::value& restore_mode, bool validate) + nmos::modify_rebuildable_block_handler modify_rebuildable_block = [&](nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const web::json::value& target_role_path, const web::json::value& property_values, bool recurse, const web::json::value& restore_mode, bool validate) { modify_rebuildable_block_called = true; value out = value::array(); @@ -298,7 +298,7 @@ BST_TEST_CASE(testApplyBackupDataSet) web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::modify; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, control_protocol_state, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -335,7 +335,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, control_protocol_state, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -375,7 +375,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, control_protocol_state, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -420,7 +420,7 @@ BST_TEST_CASE(testApplyBackupDataSet) web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, control_protocol_state, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -454,7 +454,7 @@ BST_TEST_CASE(testApplyBackupDataSet) web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, control_protocol_state, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -489,7 +489,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, control_protocol_state, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); From 66d84b16292a9553375fba05890d5378b59a9915 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 20 Dec 2024 10:26:37 +0000 Subject: [PATCH 144/250] Make method signatures consistent --- .../nmos-cpp-node/node_implementation.cpp | 4 ++-- Development/nmos/configuration_handlers.h | 4 ++-- Development/nmos/configuration_methods.cpp | 20 +++++++++---------- Development/nmos/configuration_methods.h | 6 +++--- Development/nmos/control_protocol_state.cpp | 4 ++-- .../nmos/test/configuration_methods_test.cpp | 16 +++++++-------- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 6c5897b9a..d8e166d61 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1725,7 +1725,7 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control // Example Device Configuration callback for validating a back-up dataset nmos::modify_read_only_config_properties_handler make_modify_read_only_config_properties_handler(nmos::resources& resources, slog::base_gate& gate) { - return [&resources, &gate](nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const web::json::value& target_role_path, const web::json::value& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate) + return [&resources, &gate](const web::json::value& target_role_path, const web::json::value& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do modify_read_only_config_properties"; @@ -1736,7 +1736,7 @@ nmos::modify_read_only_config_properties_handler make_modify_read_only_config_pr // Example Device Configuration callback for restoring a back-up dataset nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmos::resources& resources, slog::base_gate& gate) { - return [&resources, &gate](nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const web::json::value& target_role_path, const web::json::value& property_values, bool recurse, const web::json::value& restore_mode, bool validate) + return [&resources, &gate](const web::json::value& target_role_path, const web::json::value& property_values, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do modify_rebuildable_block"; diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h index 271664136..caeaf4429 100644 --- a/Development/nmos/configuration_handlers.h +++ b/Development/nmos/configuration_handlers.h @@ -19,12 +19,12 @@ namespace nmos } // This callback is invoked if attempting to modify read only properties when restoring a configuration. // This function should modify the Device Model object directly and return a corresponding NcObjectPropertiesSetValidation object - typedef std::function modify_read_only_config_properties_handler; + typedef std::function modify_read_only_config_properties_handler; // This callback is invoked if attempting to modify a rebuildable block when restoring a configuration. // This function should handle the modification of the Device Model and any corresponding NMOS resources // and return correpsonding NcObjectPropertiesSetValidation objects for each object modified/added - typedef std::function modify_rebuildable_block_handler; + typedef std::function modify_rebuildable_block_handler; } #endif diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index 22120b329..03c4e726e 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -258,7 +258,7 @@ namespace nmos return property_restore_notices; } - web::json::value modify_device_model(nmos::resources& resources, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const web::json::value& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value modify_device_model(nmos::resources& resources, const web::json::value& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) { auto object_properties_set_validation_values = web::json::value::array(); @@ -302,7 +302,7 @@ namespace nmos if (nmos::fields::nc::is_rebuildable(found->data) && target_object_properties_holders.size() && is_block_modified(*found, *target_object_properties_holders.begin())) { // call back to application code - return modify_rebuildable_block(get_control_protocol_class_descriptor, target_role_path, child_object_properties_holders, recurse, restore_mode, validate); + return modify_rebuildable_block(target_role_path, child_object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor); } // iterate through child objects if (found->data.has_field(nmos::fields::nc::members)) @@ -322,7 +322,7 @@ namespace nmos } web::json::push_back(child_role_path, nmos::fields::nc::role(child->data)); - web::json::value child_object_properties_set_validation_values = modify_device_model(resources, get_control_protocol_class_descriptor, child_role_path, child_object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, child_role_path, child_object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) { web::json::push_back(object_properties_set_validation_values, validation_values); @@ -367,7 +367,7 @@ namespace nmos // If this is a read only property then we should call back to the application code to // check that it's OK to change this value. Bear in mind that this could be a class Id, or an oid or some other // property that we don't want changed - const auto& object_properties_set_validation = modify_read_only_config_properties(get_control_protocol_class_descriptor, target_role_path, property_modify_list, recurse, restore_mode, validate); + const auto& object_properties_set_validation = modify_read_only_config_properties(target_role_path, property_modify_list, recurse, restore_mode, validate, get_control_protocol_class_descriptor); // add in already generated property_restore_notices auto modified_object_properties_set_validation = object_properties_set_validation; auto& notices = nmos::fields::nc::notices(modified_object_properties_set_validation); @@ -404,7 +404,7 @@ namespace nmos return object_properties_set_validation_values; } - web::json::value apply_backup_data_set(nmos::resources& resources, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) { auto object_properties_set_validation_values = web::json::value::array(); @@ -423,7 +423,7 @@ namespace nmos web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); } - web::json::value child_object_properties_set_validation_values = modify_device_model(resources, get_control_protocol_class_descriptor, target_role_path, object_properties_holders, recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, target_role_path, object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) { web::json::push_back(object_properties_set_validation_values, validation_values); @@ -432,22 +432,22 @@ namespace nmos return object_properties_set_validation_values; } - web::json::value validate_set_properties_by_path(nmos::resources& resources, 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& restore_mode, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) { // Do something with validation fingerprint? const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto& object_properties_set_validation = apply_backup_data_set(resources, get_control_protocol_class_descriptor, resource, object_properties_holders, recurse, restore_mode, true, modify_read_only_config_properties, modify_rebuildable_block); + const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } - web::json::value set_properties_by_path(nmos::resources& resources, 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& restore_mode, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) { // Do something with validation fingerprint? const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto& object_properties_set_validation = apply_backup_data_set(resources, get_control_protocol_class_descriptor, resource, object_properties_holders, recurse, restore_mode, false, modify_read_only_config_properties, modify_rebuildable_block); + const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } diff --git a/Development/nmos/configuration_methods.h b/Development/nmos/configuration_methods.h index e3a1c128d..e83b500cc 100644 --- a/Development/nmos/configuration_methods.h +++ b/Development/nmos/configuration_methods.h @@ -25,11 +25,11 @@ namespace nmos // Get role path of resource given the Device Model resources web::json::value get_role_path(const nmos::resources& resources, const nmos::resource& resource); - web::json::value apply_backup_data_set(nmos::resources& resources, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); - web::json::value validate_set_properties_by_path(nmos::resources& resources, 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& restore_mode, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); - web::json::value set_properties_by_path(nmos::resources& resources, 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& restore_mode, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); } #endif \ No newline at end of file diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 47313a7f6..35ae61db6 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -205,7 +205,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); if (modify_read_only_config_properties && modify_rebuildable_block) { - result = validate_set_properties_by_path(resources, get_control_protocol_class_descriptor, resource, data_set, recurse, restore_mode, modify_read_only_config_properties, modify_rebuildable_block); + result = validate_set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -232,7 +232,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("callbacks not implemented")); if (modify_read_only_config_properties && modify_rebuildable_block) { - result = set_properties_by_path(resources, get_control_protocol_class_descriptor, resource, data_set, recurse, restore_mode, modify_read_only_config_properties, modify_rebuildable_block); + result = set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) diff --git a/Development/nmos/test/configuration_methods_test.cpp b/Development/nmos/test/configuration_methods_test.cpp index fd8df57f5..8c7270b72 100644 --- a/Development/nmos/test/configuration_methods_test.cpp +++ b/Development/nmos/test/configuration_methods_test.cpp @@ -267,12 +267,12 @@ BST_TEST_CASE(testApplyBackupDataSet) bool modify_rebuildable_block_called = false; // callback stubs - nmos::modify_read_only_config_properties_handler modify_read_only_config_properties = [&](nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const web::json::value& target_role_path, const web::json::value& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate) + nmos::modify_read_only_config_properties_handler modify_read_only_config_properties = [&](const web::json::value& target_role_path, const web::json::value& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { modify_read_only_config_properties_called = true; return nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, value::array(), U("OK")); }; - nmos::modify_rebuildable_block_handler modify_rebuildable_block = [&](nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const web::json::value& target_role_path, const web::json::value& property_values, bool recurse, const web::json::value& restore_mode, bool validate) + nmos::modify_rebuildable_block_handler modify_rebuildable_block = [&](const web::json::value& target_role_path, const web::json::value& property_values, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { modify_rebuildable_block_called = true; value out = value::array(); @@ -298,7 +298,7 @@ BST_TEST_CASE(testApplyBackupDataSet) web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::modify; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -335,7 +335,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -375,7 +375,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -420,7 +420,7 @@ BST_TEST_CASE(testApplyBackupDataSet) web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -454,7 +454,7 @@ BST_TEST_CASE(testApplyBackupDataSet) web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -489,7 +489,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, get_control_protocol_class_descriptor, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); From 380074238131a841264f4c172513fc06263e67cc Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 20 Dec 2024 12:44:28 +0000 Subject: [PATCH 145/250] Add configuration handlers to api --- Development/nmos/configuration_api.cpp | 163 +++++++++---------------- Development/nmos/configuration_api.h | 3 +- Development/nmos/node_server.cpp | 2 +- 3 files changed, 64 insertions(+), 104 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 9e78aac6b..536fd69f2 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -18,9 +18,9 @@ namespace nmos { - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -51,7 +51,7 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, property_changed, gate)); + configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, modify_read_only_config_properties, modify_rebuildable_block, property_changed, gate)); return configuration_api; } @@ -148,7 +148,7 @@ namespace nmos } } - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -418,7 +418,7 @@ namespace nmos return pplx::task_from_result(true); }); - // GET /rolePaths/{rolePath}/properties/{propertyId}/value + // GET /rolePaths/{rolePath}/properties/{propertyId}/value - invokes get method configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/value/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto property_id = parameters.at(nmos::patterns::propertyId.name); @@ -430,17 +430,14 @@ namespace nmos const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); if (resources.end() != resource) { - // find the relevant nc_property_descriptor - const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); - if (property_descriptor.is_null()) - { - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); - } - else - { - auto method_result = details::make_nc_method_result({ nmos::fields::nc::is_deprecated(property_descriptor) ? nmos::nc_method_status::property_deprecated : nmos::nc_method_status::ok }, resource->data.at(nmos::fields::nc::name(property_descriptor))); - set_reply(res, status_codes::OK, method_result); - } + auto arguments = value_of({ + { nmos::fields::nc::id, details::make_nc_property_id(details::parse_formatted_property_id(property_id))}, + }); + + auto result = get(*resource, arguments, false, get_control_protocol_class_descriptor, gate_); + auto status = nmos::fields::nc::status(result); + auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; + set_reply(res, code, result); } else { @@ -451,7 +448,7 @@ namespace nmos return pplx::task_from_result(true); }); - // GET /rolePaths/{rolePath}/methods/{methodId} + // GET /rolePaths/{rolePath}/methods/{methodId} - invokes method specified by {methodId} configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/methods/") + nmos::patterns::methodId.pattern + U("/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, property_changed, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { nmos::api_gate gate(gate_, req, parameters); @@ -527,7 +524,7 @@ namespace nmos }); }); - // PUT /rolePaths/{rolePath}/properties/{propertyId}/value + // PUT /rolePaths/{rolePath}/properties/{propertyId}/value - invokes set method configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/value/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { nmos::api_gate gate(gate_, req, parameters); @@ -579,7 +576,7 @@ namespace nmos }); }); - // GET /rolePaths/{rolePath}/bulkProperties + // GET /rolePaths/{rolePath}/bulkProperties - invokes get_properties_by_path method configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::GET, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); @@ -625,8 +622,8 @@ namespace nmos return pplx::task_from_result(true); }); - // PATCH /rolePaths/{rolePath}/bulkProperties - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_method_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + // PATCH /rolePaths/{rolePath}/bulkProperties - invokes validation_set_properties_by_path method + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); @@ -634,48 +631,39 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); - const auto& bulk_properties_manager = find_control_protocol_resource_by_role_path(resources, nmos::bulk_properties_manager_role); - if (resources.end() != resource && resources.end() != bulk_properties_manager) + if (resources.end() != resource) { - return details::extract_json(req, gate_).then([res, resources, resource, get_control_protocol_method_descriptor, version, &gate_](value body) mutable + return details::extract_json(req, gate_).then([res, resources, resource, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block, version, &gate_](value body) mutable { // Validate JSON syntax according to the schema details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_validate_request_schema_uri(version)); - auto method = get_control_protocol_method_descriptor(nc_bulk_properties_manager_class_id, nc_bulk_properties_manager_validate_set_properties_by_path_method_id); - auto& nc_method_descriptor = method.first; - auto& control_method_handler = method.second; web::http::status_code code{ status_codes::BadRequest }; value method_result; - if (control_method_handler) + try { - try - { - method_result = control_method_handler(resources, *resource, nmos::fields::nc::arguments(body), nmos::fields::nc::is_deprecated(nc_method_descriptor), gate_); - - auto status = nmos::fields::nc::status(method_result); - if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } - else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } - else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } - else { code = status_codes::InternalError; } - } - catch (const nmos::control_protocol_exception& e) - { - // invalid arguments - utility::stringstream_t ss; - ss << U("parameter error: ") << e.what(); - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); - - code = status_codes::BadRequest; - } + const auto& arguments = nmos::fields::nc::arguments(body); + bool recurse = nmos::fields::nc::recurse(arguments); + const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); + const auto& backup_data_set = nmos::fields::nc::data_set(arguments); + + method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + + auto status = nmos::fields::nc::status(method_result); + if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } + else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } + else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } + else { code = status_codes::InternalError; } } - else + catch (const nmos::control_protocol_exception& e) { - // unknown methodId - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("validate_set_properties_by_path unsupported by bulk properties manager.")); + // invalid arguments + utility::stringstream_t ss; + ss << U("parameter error: ") << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); - code = status_codes::NotFound; + code = status_codes::BadRequest; } set_reply(res, code, method_result); @@ -684,23 +672,15 @@ namespace nmos } else { - if (resources.end() == bulk_properties_manager) - { - // no bulk properties manager - set_error_reply(res, status_codes::NotFound, U("Bulk Properties Manager not found at ") + nmos::bulk_properties_manager_role); - } - else - { - // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); - } + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); } return pplx::task_from_result(true); }); - // PUT /rolePaths/{rolePath}/bulkProperties - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_method_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + // PUT /rolePaths/{rolePath}/bulkProperties - invokes set_properties_by_path method + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); @@ -708,48 +688,35 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); - const auto& bulk_properties_manager = find_control_protocol_resource_by_role_path(resources, nmos::bulk_properties_manager_role); - if (resources.end() != resource && resources.end() != bulk_properties_manager) + if (resources.end() != resource) { - return details::extract_json(req, gate_).then([res, &resources, resource, get_control_protocol_method_descriptor, version, &model, &gate_](value body) mutable + return details::extract_json(req, gate_).then([res, &resources, resource, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block, version, &model, &gate_](value body) mutable { // Validate JSON syntax according to the schema details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_set_request_schema_uri(version)); - auto method = get_control_protocol_method_descriptor(nc_bulk_properties_manager_class_id, nc_bulk_properties_manager_set_properties_by_path_method_id); - auto& nc_method_descriptor = method.first; - auto& control_method_handler = method.second; web::http::status_code code{ status_codes::BadRequest }; value method_result; - if (control_method_handler) + try { - try - { - method_result = control_method_handler(resources, *resource, nmos::fields::nc::arguments(body), nmos::fields::nc::is_deprecated(nc_method_descriptor), gate_); - auto status = nmos::fields::nc::status(method_result); - if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } - else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } - else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } - else { code = status_codes::InternalError; } - model.notify(); - } - catch (const nmos::control_protocol_exception& e) - { - // invalid arguments - utility::stringstream_t ss; - ss << U("parameter error: ") << e.what(); - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + const auto& arguments = nmos::fields::nc::arguments(body); + bool recurse = nmos::fields::nc::recurse(arguments); + const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); + const auto& backup_data_set = nmos::fields::nc::data_set(arguments); - code = status_codes::BadRequest; - } + method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + + model.notify(); } - else + catch (const nmos::control_protocol_exception& e) { - // unknown methodId - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("set_properties_by_path unsupported by bulk properties manager.")); + // invalid arguments + utility::stringstream_t ss; + ss << U("parameter error: ") << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); - code = status_codes::NotFound; + code = status_codes::BadRequest; } set_reply(res, code, method_result); @@ -758,16 +725,8 @@ namespace nmos } else { - if (resources.end() == bulk_properties_manager) - { - // no bulk properties manager - set_error_reply(res, status_codes::NotFound, U("Bulk Properties Manager not found at ") + nmos::bulk_properties_manager_role); - } - else - { - // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); - } + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); } return pplx::task_from_result(true); diff --git a/Development/nmos/configuration_api.h b/Development/nmos/configuration_api.h index 9b1a73297..f85ecf673 100644 --- a/Development/nmos/configuration_api.h +++ b/Development/nmos/configuration_api.h @@ -3,6 +3,7 @@ #include "cpprest/api_router.h" #include "nmos/control_protocol_handlers.h" +#include "nmos/configuration_handlers.h" namespace slog { @@ -15,7 +16,7 @@ namespace nmos { struct node_model; - web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); } #endif diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index 1e9a97da2..9516d59e0 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -77,7 +77,7 @@ namespace nmos // Configure the Configuration API - node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.control_protocol_property_changed, gate)); + node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.modify_read_only_config_properties, node_implementation.modify_rebuildable_block, node_implementation.control_protocol_property_changed, gate)); const auto& events_ws_port = nmos::fields::events_ws_port(node_model.settings); auto& events_ws_api = node_server.ws_handlers[{ {}, nmos::fields::events_ws_port(node_model.settings) }]; From eac99d28289f3912bb8b038d97ff324f36c7163a Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 20 Dec 2024 12:45:00 +0000 Subject: [PATCH 146/250] Pass resource, rather than making modify_device_model search for it --- Development/nmos/configuration_methods.cpp | 168 ++++++++++----------- 1 file changed, 82 insertions(+), 86 deletions(-) diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index 03c4e726e..7ba40368c 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -258,7 +258,7 @@ namespace nmos return property_restore_notices; } - web::json::value modify_device_model(nmos::resources& resources, const web::json::value& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value modify_device_model(nmos::resources& resources, const nmos::resource& resource, const web::json::value& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) { auto object_properties_set_validation_values = web::json::value::array(); @@ -290,117 +290,113 @@ namespace nmos return object_properties_set_validation_values; } - const auto& found = nmos::find_control_protocol_resource_by_role_path(resources, target_role_path); + nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); - if (resources.end() != found) + if (nmos::is_nc_block(class_id)) { - nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)); - - if (nmos::is_nc_block(class_id)) + // if rebuildable and the block has changed then callback + if (nmos::fields::nc::is_rebuildable(resource.data) && target_object_properties_holders.size() && is_block_modified(resource, *target_object_properties_holders.begin())) { - // if rebuildable and the block has changed then callback - if (nmos::fields::nc::is_rebuildable(found->data) && target_object_properties_holders.size() && is_block_modified(*found, *target_object_properties_holders.begin())) - { - // call back to application code - return modify_rebuildable_block(target_role_path, child_object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor); - } - // iterate through child objects - if (found->data.has_field(nmos::fields::nc::members)) + // call back to application code + return modify_rebuildable_block(target_role_path, child_object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor); + } + // iterate through child objects + if (resource.data.has_field(nmos::fields::nc::members)) + { + const auto& members = nmos::fields::nc::members(resource.data); + + for (const auto& member : members) { - const auto& members = nmos::fields::nc::members(found->data); + const auto& child = find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(member)))); - for (const auto& member : members) + if (resources.end() != child) { - const auto& child = find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(member)))); - - if (resources.end() != child) + auto child_role_path = web::json::value::array(); + for (const auto& path_element : target_role_path.as_array()) { - auto child_role_path = web::json::value::array(); - for (const auto& path_element : target_role_path.as_array()) - { - web::json::push_back(child_role_path, path_element); - } - web::json::push_back(child_role_path, nmos::fields::nc::role(child->data)); + web::json::push_back(child_role_path, path_element); + } + web::json::push_back(child_role_path, nmos::fields::nc::role(child->data)); - web::json::value child_object_properties_set_validation_values = modify_device_model(resources, child_role_path, child_object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); - for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) - { - web::json::push_back(object_properties_set_validation_values, validation_values); - } + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, *child, child_role_path, child_object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) + { + web::json::push_back(object_properties_set_validation_values, validation_values); } } } } - for (const auto& target_object_properties_holder : target_object_properties_holders) + } + for (const auto& target_object_properties_holder : target_object_properties_holders) + { + auto property_restore_notices = web::json::value::array(); + auto property_modify_list = web::json::value::array(); + unsigned int rebuildable_property_count = 0; + // Validate property_values - filter out the incorrect, ignored or unallowed + for (const auto& property_value : nmos::fields::nc::values(target_object_properties_holder)) { - auto property_restore_notices = web::json::value::array(); - auto property_modify_list = web::json::value::array(); - unsigned int rebuildable_property_count = 0; - // Validate property_values - filter out the incorrect, ignored or unallowed - for (const auto& property_value : nmos::fields::nc::values(target_object_properties_holder)) + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_descriptor = nmos::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); + const auto& property_restore_notices_ = check_property_value(property_value, property_descriptor, restore_mode); + if (property_restore_notices_.size() > 0) { - const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); - const auto& property_descriptor = nmos::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); - const auto& property_restore_notices_ = check_property_value(property_value, property_descriptor, restore_mode); - if (property_restore_notices_.size() > 0) + for (const auto& notice : property_restore_notices_.as_array()) { - for (const auto& notice : property_restore_notices_.as_array()) - { - web::json::push_back(property_restore_notices, notice); - } - continue; + web::json::push_back(property_restore_notices, notice); } - // Ignore if no change is being requested - if (found->data.at(nmos::fields::nc::name(property_descriptor)) == nmos::fields::nc::value(property_value)) - { - continue; - } - // Only allow modification of read only properties when in Rebuild mode - if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && restore_mode == nmos::nc_restore_mode::restore_mode::rebuild) - { - rebuildable_property_count++; - } - web::json::push_back(property_modify_list, property_value); + continue; } - if (rebuildable_property_count > 0 && property_modify_list.as_array().size() > 0) + // Ignore if no change is being requested + if (resource.data.at(nmos::fields::nc::name(property_descriptor)) == nmos::fields::nc::value(property_value)) { - // If this is a read only property then we should call back to the application code to - // check that it's OK to change this value. Bear in mind that this could be a class Id, or an oid or some other - // property that we don't want changed - const auto& object_properties_set_validation = modify_read_only_config_properties(target_role_path, property_modify_list, recurse, restore_mode, validate, get_control_protocol_class_descriptor); - // add in already generated property_restore_notices - auto modified_object_properties_set_validation = object_properties_set_validation; - auto& notices = nmos::fields::nc::notices(modified_object_properties_set_validation); - for (const auto& notice : property_restore_notices.as_array()) - { - web::json::push_back(notices, notice); - } - - web::json::push_back(object_properties_set_validation_values, modified_object_properties_set_validation); + continue; } - else + // Only allow modification of read only properties when in Rebuild mode + if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && restore_mode == nmos::nc_restore_mode::restore_mode::rebuild) { - for (const auto& property_value : property_modify_list.as_array()) - { - const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + rebuildable_property_count++; + } + web::json::push_back(property_modify_list, property_value); + } + if (rebuildable_property_count > 0 && property_modify_list.as_array().size() > 0) + { + // If this is a read only property then we should call back to the application code to + // check that it's OK to change this value. Bear in mind that this could be a class Id, or an oid or some other + // property that we don't want changed + const auto& object_properties_set_validation = modify_read_only_config_properties(target_role_path, property_modify_list, recurse, restore_mode, validate, get_control_protocol_class_descriptor); + // add in already generated property_restore_notices + auto modified_object_properties_set_validation = object_properties_set_validation; + auto& notices = nmos::fields::nc::notices(modified_object_properties_set_validation); + for (const auto& notice : property_restore_notices.as_array()) + { + web::json::push_back(notices, notice); + } - if (!validate) - { - // modify control protocol resources - const auto& value = nmos::fields::nc::value(property_value); + web::json::push_back(object_properties_set_validation_values, modified_object_properties_set_validation); + } + else + { + for (const auto& property_value : property_modify_list.as_array()) + { + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); - modify_control_protocol_resource(resources, found->id, [&](nmos::resource& resource_) - { - resource_.data[nmos::fields::nc::name(property_value)] = value; + if (!validate) + { + // modify control protocol resources + const auto& value = nmos::fields::nc::value(property_value); - }, nmos::make_property_changed_event(nmos::fields::nc::oid(found->data), {{property_id, nmos::nc_property_change_type::type::value_changed, value}})); - } + modify_control_protocol_resource(resources, resource.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}})); } - const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_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); } + const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_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 object_properties_set_validation_values; } @@ -423,7 +419,7 @@ namespace nmos web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); } - web::json::value child_object_properties_set_validation_values = modify_device_model(resources, target_role_path, object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, resource, target_role_path, object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) { web::json::push_back(object_properties_set_validation_values, validation_values); From a34db05527b6bbc15dd71f9779ab703811ba56ac Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 20 Dec 2024 17:25:08 +0000 Subject: [PATCH 147/250] Create example filter_property_value_holders_handler --- Development/nmos-cpp-node/main.cpp | 2 +- .../nmos-cpp-node/node_implementation.cpp | 35 ++++- Development/nmos/configuration_api.cpp | 20 +-- Development/nmos/configuration_api.h | 2 +- Development/nmos/configuration_handlers.h | 4 +- Development/nmos/configuration_methods.cpp | 84 +++++----- Development/nmos/configuration_methods.h | 6 +- Development/nmos/control_protocol_state.cpp | 22 +-- Development/nmos/control_protocol_state.h | 2 +- Development/nmos/node_server.cpp | 2 +- Development/nmos/node_server.h | 8 +- .../nmos/test/configuration_methods_test.cpp | 145 ++++++++++++++---- 12 files changed, 218 insertions(+), 114 deletions(-) diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index d9bf0a6c5..9e710aa15 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -138,7 +138,7 @@ int main(int argc, char* argv[]) .on_request_authorization_code(nmos::experimental::make_request_authorization_code_handler(gate)); // may be omitted, only required for OAuth client which is using the Authorization Code Flow to obtain the access token } - nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.modify_read_only_config_properties, node_implementation.modify_rebuildable_block); + nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.filter_property_value_holders, node_implementation.modify_rebuildable_block); if (0 <= nmos::fields::control_protocol_ws_port(node_model.settings)) { node_implementation diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index d8e166d61..3da8366b8 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1723,20 +1723,43 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control } // Example Device Configuration callback for validating a back-up dataset -nmos::modify_read_only_config_properties_handler make_modify_read_only_config_properties_handler(nmos::resources& resources, slog::base_gate& gate) +nmos::filter_property_value_holders_handler make_filter_property_value_holders_handler(nmos::resources& resources, slog::base_gate& gate) { - return [&resources, &gate](const web::json::value& target_role_path, const web::json::value& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + return [&resources, &gate](const nmos::resource& resource, const web::json::value& target_role_path, const web::json::value& property_values, bool recurse, const web::json::value& restore_mode, bool validate, web::json::value& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { - slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do modify_read_only_config_properties"; + // Use this function to filter which of the properties in the object should be modified by the configuration API + slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do filter_property_value_holders"; - return web::json::value(); + nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + + auto modifiable_property_value_holders = web::json::value::array(); + + for (const auto property_value : property_values.as_array()) + { + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_descriptor = nmos::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); + + // In this example we are only allowing writable properties to be modified + if (bool(nmos::fields::nc::is_read_only(property_descriptor))) + { + // We need to create a notice for any properties that will not be updated + 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("can not update read only properties")); + web::json::push_back(property_restore_notices, property_restore_notice); + } + else + { + web::json::push_back(modifiable_property_value_holders, property_value); + } + } + + return modifiable_property_value_holders; }; } // Example Device Configuration callback for restoring a back-up dataset nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmos::resources& resources, slog::base_gate& gate) { - return [&resources, &gate](const web::json::value& target_role_path, const web::json::value& property_values, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + return [&resources, &gate](const nmos::resource& resource, const web::json::value& target_role_path, const web::json::value& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do modify_rebuildable_block"; @@ -1899,6 +1922,6 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode .on_validate_channelmapping_output_map(make_node_implementation_map_validator()) // may be omitted if not required .on_channelmapping_activated(make_node_implementation_channelmapping_activation_handler(gate)) .on_control_protocol_property_changed(make_node_implementation_control_protocol_property_changed_handler(gate)) // may be omitted if IS-12 not required - .on_modify_read_only_config_properties(make_modify_read_only_config_properties_handler(model.control_protocol_resources, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required + .on_filter_property_value_holders(make_filter_property_value_holders_handler(model.control_protocol_resources, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required .on_modify_rebuildable_block(make_modify_rebuildable_block_handler(model.control_protocol_resources, gate)); // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required } diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 536fd69f2..9aacb6730 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -18,9 +18,9 @@ namespace nmos { - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -51,7 +51,7 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, modify_read_only_config_properties, modify_rebuildable_block, property_changed, gate)); + configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, filter_property_value_holders, modify_rebuildable_block, property_changed, gate)); return configuration_api; } @@ -148,7 +148,7 @@ namespace nmos } } - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -623,7 +623,7 @@ namespace nmos }); // PATCH /rolePaths/{rolePath}/bulkProperties - invokes validation_set_properties_by_path method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); @@ -633,7 +633,7 @@ namespace nmos const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); if (resources.end() != resource) { - return details::extract_json(req, gate_).then([res, resources, resource, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block, version, &gate_](value body) mutable + return details::extract_json(req, gate_).then([res, resources, resource, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, version, &gate_](value body) mutable { // Validate JSON syntax according to the schema details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_validate_request_schema_uri(version)); @@ -648,7 +648,7 @@ namespace nmos const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); const auto& backup_data_set = nmos::fields::nc::data_set(arguments); - method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); auto status = nmos::fields::nc::status(method_result); if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } @@ -680,7 +680,7 @@ namespace nmos }); // PUT /rolePaths/{rolePath}/bulkProperties - invokes set_properties_by_path method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); @@ -690,7 +690,7 @@ namespace nmos const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); if (resources.end() != resource) { - return details::extract_json(req, gate_).then([res, &resources, resource, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block, version, &model, &gate_](value body) mutable + return details::extract_json(req, gate_).then([res, &resources, resource, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, version, &model, &gate_](value body) mutable { // Validate JSON syntax according to the schema details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_set_request_schema_uri(version)); @@ -705,7 +705,7 @@ namespace nmos const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); const auto& backup_data_set = nmos::fields::nc::data_set(arguments); - method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); model.notify(); } diff --git a/Development/nmos/configuration_api.h b/Development/nmos/configuration_api.h index f85ecf673..5bf4df971 100644 --- a/Development/nmos/configuration_api.h +++ b/Development/nmos/configuration_api.h @@ -16,7 +16,7 @@ namespace nmos { struct node_model; - web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); } #endif diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h index caeaf4429..9c9eca851 100644 --- a/Development/nmos/configuration_handlers.h +++ b/Development/nmos/configuration_handlers.h @@ -19,12 +19,12 @@ namespace nmos } // This callback is invoked if attempting to modify read only properties when restoring a configuration. // This function should modify the Device Model object directly and return a corresponding NcObjectPropertiesSetValidation object - typedef std::function modify_read_only_config_properties_handler; + typedef std::function filter_property_value_holders_handler; // This callback is invoked if attempting to modify a rebuildable block when restoring a configuration. // This function should handle the modification of the Device Model and any corresponding NMOS resources // and return correpsonding NcObjectPropertiesSetValidation objects for each object modified/added - typedef std::function modify_rebuildable_block_handler; + typedef std::function modify_rebuildable_block_handler; } #endif diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index 7ba40368c..c5a7ac9e9 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -227,12 +227,10 @@ namespace nmos return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, bulk_values_holder); } - web::json::value check_property_value(const web::json::value& property_value, const web::json::value& property_descriptor, const web::json::value& restore_mode) + bool is_property_value_valid(const web::json::value& property_value, const web::json::value& property_descriptor, const web::json::value& restore_mode, web::json::value& property_restore_notices) { const nmos::nc_property_id& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)); - - auto property_restore_notices = web::json::value::array(); - + bool is_valid = true; // Check the name of the property is correct if (nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::name(property_value)) { @@ -240,6 +238,7 @@ namespace nmos os << U("unexpected property name: expected ") << nmos::fields::nc::name(property_descriptor) << U(", actual ") << nmos::fields::nc::name(property_value); 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::error, os.str()); web::json::push_back(property_restore_notices, property_restore_notice); + is_valid = false; } // Check the type of the property value is correct if (nmos::fields::nc::type_name(property_descriptor) != nmos::fields::nc::type_name(property_value)) @@ -248,17 +247,19 @@ namespace nmos os << U("unexpected property type: expected ") << nmos::fields::nc::type_name(property_descriptor) << U(", actual ") << nmos::fields::nc::type_name(property_value); 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::error, os.str()); web::json::push_back(property_restore_notices, property_restore_notice); + is_valid = false; } // Only allow modification of read only properties when in Rebuild mode if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && restore_mode != nmos::nc_restore_mode::restore_mode::rebuild) { 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::error, U("read only properties can not be modified in Modify restore mode.")); web::json::push_back(property_restore_notices, property_restore_notice); + is_valid = false; } - return property_restore_notices; + return is_valid; } - web::json::value modify_device_model(nmos::resources& resources, const nmos::resource& resource, const web::json::value& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value modify_device_model(nmos::resources& resources, const nmos::resource& resource, const web::json::value& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) { auto object_properties_set_validation_values = web::json::value::array(); @@ -298,7 +299,7 @@ namespace nmos if (nmos::fields::nc::is_rebuildable(resource.data) && target_object_properties_holders.size() && is_block_modified(resource, *target_object_properties_holders.begin())) { // call back to application code - return modify_rebuildable_block(target_role_path, child_object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor); + return modify_rebuildable_block(resource, target_role_path, child_object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor); } // iterate through child objects if (resource.data.has_field(nmos::fields::nc::members)) @@ -311,6 +312,8 @@ namespace nmos if (resources.end() != child) { + // Apend the role of the child to the target role path to create the child role path + // Hmmmm, there must be a better way of appending the child role to the end of the target role path array... auto child_role_path = web::json::value::array(); for (const auto& path_element : target_role_path.as_array()) { @@ -318,7 +321,8 @@ namespace nmos } web::json::push_back(child_role_path, nmos::fields::nc::role(child->data)); - web::json::value child_object_properties_set_validation_values = modify_device_model(resources, *child, child_role_path, child_object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, *child, child_role_path, child_object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + // Hmmm, there must be a better way of marging two json array objects for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) { web::json::push_back(object_properties_set_validation_values, validation_values); @@ -331,19 +335,14 @@ namespace nmos { auto property_restore_notices = web::json::value::array(); auto property_modify_list = web::json::value::array(); - unsigned int rebuildable_property_count = 0; + auto read_only_property_modify_list = web::json::value::array(); // Validate property_values - filter out the incorrect, ignored or unallowed for (const auto& property_value : nmos::fields::nc::values(target_object_properties_holder)) { const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); const auto& property_descriptor = nmos::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); - const auto& property_restore_notices_ = check_property_value(property_value, property_descriptor, restore_mode); - if (property_restore_notices_.size() > 0) + if(!is_property_value_valid(property_value, property_descriptor, restore_mode, property_restore_notices)) { - for (const auto& notice : property_restore_notices_.as_array()) - { - web::json::push_back(property_restore_notices, notice); - } continue; } // Ignore if no change is being requested @@ -354,53 +353,42 @@ namespace nmos // Only allow modification of read only properties when in Rebuild mode if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && restore_mode == nmos::nc_restore_mode::restore_mode::rebuild) { - rebuildable_property_count++; + push_back(read_only_property_modify_list, property_value); } web::json::push_back(property_modify_list, property_value); } - if (rebuildable_property_count > 0 && property_modify_list.as_array().size() > 0) + + if (filter_property_value_holders && read_only_property_modify_list.as_array().size() > 0) { // If this is a read only property then we should call back to the application code to // check that it's OK to change this value. Bear in mind that this could be a class Id, or an oid or some other // property that we don't want changed - const auto& object_properties_set_validation = modify_read_only_config_properties(target_role_path, property_modify_list, recurse, restore_mode, validate, get_control_protocol_class_descriptor); - // add in already generated property_restore_notices - auto modified_object_properties_set_validation = object_properties_set_validation; - auto& notices = nmos::fields::nc::notices(modified_object_properties_set_validation); - for (const auto& notice : property_restore_notices.as_array()) - { - web::json::push_back(notices, notice); - } - - web::json::push_back(object_properties_set_validation_values, modified_object_properties_set_validation); + property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list, recurse, restore_mode, validate, property_restore_notices, get_control_protocol_class_descriptor); } - else + for (const auto& property_value : property_modify_list.as_array()) { - for (const auto& property_value : property_modify_list.as_array()) - { - const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); - if (!validate) - { - // modify control protocol resources - const auto& value = nmos::fields::nc::value(property_value); + if (!validate) + { + // modify control protocol resources + const auto& value = nmos::fields::nc::value(property_value); - modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource_) - { - resource_.data[nmos::fields::nc::name(property_value)] = value; + modify_control_protocol_resource(resources, resource.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}})); - } + }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), {{property_id, nmos::nc_property_change_type::type::value_changed, value}})); } - const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_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); } + const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_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 object_properties_set_validation_values; } - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) { auto object_properties_set_validation_values = web::json::value::array(); @@ -419,7 +407,7 @@ namespace nmos web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); } - web::json::value child_object_properties_set_validation_values = modify_device_model(resources, resource, target_role_path, object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, resource, target_role_path, object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) { web::json::push_back(object_properties_set_validation_values, validation_values); @@ -428,22 +416,22 @@ namespace nmos return object_properties_set_validation_values; } - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) { // Do something with validation fingerprint? const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } - web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) { // Do something with validation fingerprint? const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } diff --git a/Development/nmos/configuration_methods.h b/Development/nmos/configuration_methods.h index e83b500cc..10f22ef34 100644 --- a/Development/nmos/configuration_methods.h +++ b/Development/nmos/configuration_methods.h @@ -25,11 +25,11 @@ namespace nmos // Get role path of resource given the Device Model resources web::json::value get_role_path(const nmos::resources& resources, const nmos::resource& resource); - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); - web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block); + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); } #endif \ No newline at end of file diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 35ae61db6..e775950ab 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -189,9 +189,9 @@ namespace nmos return nmos::get_properties_by_path(resources, resource, recurse, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); }; } - nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block) + nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block) { - return [&get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, modify_read_only_config_properties, modify_rebuildable_block](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [&get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, filter_property_value_holders, modify_rebuildable_block](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -203,9 +203,9 @@ namespace nmos } auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); - if (modify_read_only_config_properties && modify_rebuildable_block) + if (filter_property_value_holders && modify_rebuildable_block) { - result = validate_set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + result = validate_set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -216,9 +216,9 @@ namespace nmos return result; }; } - nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block) + nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block) { - return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, modify_read_only_config_properties, modify_rebuildable_block](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, filter_property_value_holders, modify_rebuildable_block](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -230,9 +230,9 @@ namespace nmos } auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("callbacks not implemented")); - if (modify_read_only_config_properties && modify_rebuildable_block) + if (filter_property_value_holders && modify_rebuildable_block) { - result = set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + result = set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -245,7 +245,7 @@ namespace nmos } } - control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, modify_read_only_config_properties_handler modify_read_only_config_properties, modify_rebuildable_block_handler modify_rebuildable_block) + control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block) { auto to_vector = [](const web::json::value& data) { @@ -384,8 +384,8 @@ namespace nmos to_methods_vector(make_nc_bulk_properties_manager_methods(), { { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this))}, - { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), modify_read_only_config_properties, modify_rebuildable_block) }, - { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), modify_read_only_config_properties, modify_rebuildable_block) } + { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), filter_property_value_holders, modify_rebuildable_block) }, + { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), filter_property_value_holders, modify_rebuildable_block) } }), to_vector(make_nc_bulk_properties_manager_events())) } }; diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index d89ac61a2..658336932 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -59,7 +59,7 @@ namespace nmos nmos::read_lock read_lock() const { return nmos::read_lock{ mutex }; } nmos::write_lock write_lock() const { return nmos::write_lock{ mutex }; } - control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, modify_read_only_config_properties_handler modify_read_only_config_properties = nullptr, modify_rebuildable_block_handler modify_rebuildable_block = nullptr); + control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, filter_property_value_holders_handler filter_property_value_holders = nullptr, modify_rebuildable_block_handler modify_rebuildable_block = nullptr); // insert control class descriptor, false if class descriptor already inserted bool insert(const experimental::control_class_descriptor& control_class_descriptor); // erase control class of the given class id, false if the required class not found diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index 9516d59e0..eba159d33 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -77,7 +77,7 @@ namespace nmos // Configure the Configuration API - node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.modify_read_only_config_properties, node_implementation.modify_rebuildable_block, node_implementation.control_protocol_property_changed, gate)); + node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.filter_property_value_holders, node_implementation.modify_rebuildable_block, node_implementation.control_protocol_property_changed, gate)); const auto& events_ws_port = nmos::fields::events_ws_port(node_model.settings); auto& events_ws_api = node_server.ws_handlers[{ {}, nmos::fields::events_ws_port(node_model.settings) }]; diff --git a/Development/nmos/node_server.h b/Development/nmos/node_server.h index 1c48b25a0..b4e7e9106 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -28,7 +28,7 @@ namespace nmos // underlying implementation into the server instance for the NMOS Node struct node_implementation { - node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::modify_read_only_config_properties_handler modify_read_only_config_properties, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) : load_server_certificates(std::move(load_server_certificates)) , load_dh_param(std::move(load_dh_param)) , load_ca_certificates(std::move(load_ca_certificates)) @@ -51,7 +51,7 @@ namespace nmos , get_control_protocol_datatype_descriptor(std::move(get_control_protocol_datatype_descriptor)) , get_control_protocol_method_descriptor(std::move(get_control_protocol_method_descriptor)) , control_protocol_property_changed(std::move(control_protocol_property_changed)) - , modify_read_only_config_properties(std::move(modify_read_only_config_properties)) + , filter_property_value_holders(std::move(filter_property_value_holders)) , modify_rebuildable_block(std::move(modify_rebuildable_block)) {} @@ -85,7 +85,7 @@ namespace nmos node_implementation& on_get_control_datatype_descriptor(nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { this->get_control_protocol_datatype_descriptor = std::move(get_control_protocol_datatype_descriptor); return *this; } node_implementation& on_get_control_protocol_method_descriptor(nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor) { this->get_control_protocol_method_descriptor = std::move(get_control_protocol_method_descriptor); return *this; } node_implementation& on_control_protocol_property_changed(nmos::control_protocol_property_changed_handler control_protocol_property_changed) { this->control_protocol_property_changed = std::move(control_protocol_property_changed); return *this; } - node_implementation& on_modify_read_only_config_properties(nmos::modify_read_only_config_properties_handler modify_read_only_config_properties) { this->modify_read_only_config_properties = std::move(modify_read_only_config_properties); return *this; } + node_implementation& on_filter_property_value_holders(nmos::filter_property_value_holders_handler filter_property_value_holders) { this->filter_property_value_holders = std::move(filter_property_value_holders); return *this; } node_implementation& on_modify_rebuildable_block(nmos::modify_rebuildable_block_handler modify_rebuildable_block) { this->modify_rebuildable_block = std::move(modify_rebuildable_block); return *this; } // deprecated, use on_validate_connection_resource_patch @@ -131,7 +131,7 @@ namespace nmos nmos::control_protocol_property_changed_handler control_protocol_property_changed; // Device Configuration handlers - nmos::modify_read_only_config_properties_handler modify_read_only_config_properties; + nmos::filter_property_value_holders_handler filter_property_value_holders; nmos::modify_rebuildable_block_handler modify_rebuildable_block; }; diff --git a/Development/nmos/test/configuration_methods_test.cpp b/Development/nmos/test/configuration_methods_test.cpp index 8c7270b72..77692a5b6 100644 --- a/Development/nmos/test/configuration_methods_test.cpp +++ b/Development/nmos/test/configuration_methods_test.cpp @@ -263,16 +263,22 @@ BST_TEST_CASE(testApplyBackupDataSet) insert_resource(resources, std::move(monitor1)); insert_resource(resources, std::move(monitor2)); - bool modify_read_only_config_properties_called = false; + bool filter_property_value_holders_called = false; bool modify_rebuildable_block_called = false; // callback stubs - nmos::modify_read_only_config_properties_handler modify_read_only_config_properties = [&](const web::json::value& target_role_path, const web::json::value& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::filter_property_value_holders_handler filter_property_value_holders = [&](const nmos::resource& resource, const web::json::value& target_role_path, const web::json::value& property_values, bool recurse, const web::json::value& restore_mode, bool validate, web::json::value& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { - modify_read_only_config_properties_called = true; - return nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, value::array(), U("OK")); + filter_property_value_holders_called = true; + auto modifiable_property_value_holders = web::json::value::array(); + + for (const auto property_value : property_values.as_array()) + { + web::json::push_back(modifiable_property_value_holders, property_value); + } + return modifiable_property_value_holders; }; - nmos::modify_rebuildable_block_handler modify_rebuildable_block = [&](const web::json::value& target_role_path, const web::json::value& property_values, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::modify_rebuildable_block_handler modify_rebuildable_block = [&](const nmos::resource& resource, const web::json::value& target_role_path, const web::json::value& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { modify_rebuildable_block_called = true; value out = value::array(); @@ -298,7 +304,7 @@ BST_TEST_CASE(testApplyBackupDataSet) web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::modify; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -309,13 +315,13 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); // not expecting callbacks to be invoked as no read only properties, or rebuildable blocks modified - BST_CHECK(!modify_read_only_config_properties_called); + BST_CHECK(!filter_property_value_holders_called); BST_CHECK(!modify_rebuildable_block_called); } { - // Check modify_read_only_config_properties_handler is called when changing a read only property in Rebuild mode + // Check filter_property_value_holders_handler is called when changing a read only property in Rebuild mode // - modify_read_only_config_properties_called = false; + filter_property_value_holders_called = false; modify_rebuildable_block_called = false; // Create Object Properties Holder @@ -335,7 +341,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -345,15 +351,15 @@ BST_TEST_CASE(testApplyBackupDataSet) // make sure the validation status propagates from the callback BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); - // expecting callback to modify_read_only_config_properties_called + // expecting callback to filter_property_value_holders_called // but not to modify_rebuildable_block_called - BST_CHECK(modify_read_only_config_properties_called); + BST_CHECK(filter_property_value_holders_called); BST_CHECK(!modify_rebuildable_block_called); } { // Check an error is caused by trying to modify a read only property in Modify mode // - modify_read_only_config_properties_called = false; + filter_property_value_holders_called = false; modify_rebuildable_block_called = false; // Change a read only property in Rebuild mode @@ -375,7 +381,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -396,13 +402,13 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); - BST_CHECK(!modify_read_only_config_properties_called); + BST_CHECK(!filter_property_value_holders_called); BST_CHECK(!modify_rebuildable_block_called); } { // Check modify_rebuildable_block_handler is called when trying to modify a rebuildable block // - modify_read_only_config_properties_called = false; + filter_property_value_holders_called = false; modify_rebuildable_block_called = false; // Create Object Properties Holder @@ -420,7 +426,7 @@ BST_TEST_CASE(testApplyBackupDataSet) web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -430,13 +436,13 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); - BST_CHECK(!modify_read_only_config_properties_called); + BST_CHECK(!filter_property_value_holders_called); BST_CHECK(modify_rebuildable_block_called); } { // Check that role paths outside of the scope of the target role path are errored // - modify_read_only_config_properties_called = false; + filter_property_value_holders_called = false; modify_rebuildable_block_called = false; // Create Object Properties Holder @@ -454,7 +460,7 @@ BST_TEST_CASE(testApplyBackupDataSet) web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -464,13 +470,13 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_restore_validation_status::not_found, nmos::fields::nc::status(object_properties_set_validation)); BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); - BST_CHECK(!modify_read_only_config_properties_called); + BST_CHECK(!filter_property_value_holders_called); BST_CHECK(!modify_rebuildable_block_called); } { - // Mixture of modify_read_only_config_properties_handler and errors in Rebuild mode + // Mixture of filter_property_value_holders_handler and errors in Rebuild mode // - modify_read_only_config_properties_called = false; + filter_property_value_holders_called = false; modify_rebuildable_block_called = false; // Create Object Properties Holder @@ -489,7 +495,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, modify_read_only_config_properties, modify_rebuildable_block); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -508,11 +514,98 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); - // expecting callback to modify_read_only_config_properties_called + // expecting callback to filter_property_value_holders_called // but not to modify_rebuildable_block_called - BST_CHECK(modify_read_only_config_properties_called); + BST_CHECK(filter_property_value_holders_called); BST_CHECK(!modify_rebuildable_block_called); } + { + // Incorrect property name in property value holders + // + filter_property_value_holders_called = false; + modify_rebuildable_block_called = false; + + // Create Object Properties Holder + value object_properties_holders = value::array(); + value role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + value property_value_holders = value::array(); + // This is a read only property + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("wrong_property_name"), U("NcString"), false, value("change this value"))); //read only + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false)); + // must be a more efficient way of initializing these role paths + value target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; + bool validate = true; + + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + + value object_properties_set_validation = output.as_array().at(0); + + const auto& property_restore_notices = nmos::fields::nc::notices(object_properties_set_validation); + // make sure the validation status propagates from the callback + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + + BST_REQUIRE_EQUAL(1, property_restore_notices.size()); + + const auto notice = *property_restore_notices.begin(); + BST_CHECK_EQUAL(nmos::nc_property_id(3, 2), nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); + BST_CHECK_EQUAL(U("wrong_property_name"), nmos::fields::nc::name(notice)); + BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); + BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); + + // expecting callback to filter_property_value_holders_called + // but not to modify_rebuildable_block_called + BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!modify_rebuildable_block_called); + } + { + // Incorrect property type in property value holders + // + filter_property_value_holders_called = false; + modify_rebuildable_block_called = false; + // Create Object Properties Holder + value object_properties_holders = value::array(); + value role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + value property_value_holders = value::array(); + // This is a read only property + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("wrong_data_type"), false, value("change this value"))); //read only + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false)); + // must be a more efficient way of initializing these role paths + value target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; + bool validate = true; + + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + + value object_properties_set_validation = output.as_array().at(0); + + const auto& property_restore_notices = nmos::fields::nc::notices(object_properties_set_validation); + // make sure the validation status propagates from the callback + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + + BST_REQUIRE_EQUAL(1, property_restore_notices.size()); + + const auto notice = *property_restore_notices.begin(); + BST_CHECK_EQUAL(nmos::nc_property_id(3, 2), nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); + BST_CHECK_EQUAL(U("connectionStatusMessage"), nmos::fields::nc::name(notice)); + BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); + BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); + + // expecting callback to filter_property_value_holders_called + // but not to modify_rebuildable_block_called + BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!modify_rebuildable_block_called); + } // ensure an error if trying to invoke rebuildable block when in Modify mode } \ No newline at end of file From 18f3fd482294b9f90cebf0b7e09bf4495dad20ca Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Thu, 2 Jan 2025 16:12:52 +0000 Subject: [PATCH 148/250] Moved util functions from configuration_methods to configuration_utils --- Development/cmake/NmosCppLibraries.cmake | 2 + Development/cmake/NmosCppTest.cmake | 2 +- Development/nmos/configuration_methods.cpp | 291 +---------------- Development/nmos/configuration_methods.h | 10 - Development/nmos/configuration_utils.cpp | 306 ++++++++++++++++++ Development/nmos/configuration_utils.h | 23 ++ ..._test.cpp => configuration_utils_test.cpp} | 2 +- 7 files changed, 334 insertions(+), 302 deletions(-) create mode 100644 Development/nmos/configuration_utils.cpp create mode 100644 Development/nmos/configuration_utils.h rename Development/nmos/test/{configuration_methods_test.cpp => configuration_utils_test.cpp} (99%) diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index e71af5ef2..7cbb33dbb 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -1007,6 +1007,7 @@ set(NMOS_CPP_NMOS_SOURCES nmos/components.cpp nmos/configuration_api.cpp nmos/configuration_methods.cpp + nmos/configuration_utils.cpp nmos/connection_activation.cpp nmos/connection_api.cpp nmos/connection_events_activation.cpp @@ -1103,6 +1104,7 @@ set(NMOS_CPP_NMOS_HEADERS nmos/configuration_api.h nmos/configuration_handlers.h nmos/configuration_methods.h + nmos/configuration_utils.h nmos/connection_activation.h nmos/connection_api.h nmos/connection_events_activation.h diff --git a/Development/cmake/NmosCppTest.cmake b/Development/cmake/NmosCppTest.cmake index 4fd2fc00b..ad0eba2a8 100644 --- a/Development/cmake/NmosCppTest.cmake +++ b/Development/cmake/NmosCppTest.cmake @@ -43,7 +43,7 @@ set(NMOS_CPP_TEST_NMOS_TEST_SOURCES nmos/test/api_utils_test.cpp nmos/test/capabilities_test.cpp nmos/test/channels_test.cpp - nmos/test/configuration_methods_test.cpp + nmos/test/configuration_utils_test.cpp nmos/test/control_protocol_test.cpp nmos/test/did_sdid_test.cpp nmos/test/event_type_test.cpp diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index c5a7ac9e9..6c47a99d0 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -3,6 +3,7 @@ #include #include "cpprest/json_utils.h" #include "nmos/configuration_handlers.h" +#include "nmos/configuration_utils.h" #include "nmos/control_protocol_resource.h" #include "nmos/control_protocol_resources.h" #include "nmos/control_protocol_state.h" @@ -107,107 +108,6 @@ namespace nmos } } - 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 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()) - { - 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); - web::json::push_back(role_path, nmos::fields::nc::role(found_resource.data)); - oid = nmos::fields::nc::id(found_resource.data); - } - - std::reverse(role_path.as_array().begin(), role_path.as_array().end()); - - return role_path; - } - - // Check to see if root_role_path is root of role_path_ - bool is_role_path_root(const web::json::value& role_path_root, const web::json::value& role_path_) - { - if (role_path_root.as_array().size() > role_path_.as_array().size()) - { - // root can't be longed that the path - return false; - } - for (size_t i = 0; i < role_path_root.as_array().size(); ++i) - { - if (role_path_root.as_array().at(i) != role_path_.as_array().at(i)) - { - return false; - } - } - return true; - } - - bool is_block_modified(const nmos::resource& resource, const web::json::value& object_properties_holder) - { - // Are they blocks? - nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); - if (!nmos::is_nc_block(class_id)) - { - return false; - } - const auto& block_members_properties_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) - | boost::adaptors::filtered([](const web::json::value& property_value_holder) - { - return nmos::nc_property_id(2, 2) == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value_holder)); - }) - ); - // There should only be a single property holder for the members - if (block_members_properties_holders.size() == 1) - { - const auto& members_property_holder = *block_members_properties_holders.begin(); - const auto& restore_members = nmos::fields::nc::value(members_property_holder); - const auto& reference_members = nmos::fields::nc::members(resource.data); - - if (reference_members.size() != restore_members.as_array().size()) - { - return true; - } - for (const auto& reference_member : reference_members) - { - const auto& filtered_members = boost::copy_range>(restore_members.as_array() - | boost::adaptors::filtered([&reference_member](const web::json::value& member) - { - return nmos::fields::nc::oid(reference_member) == nmos::fields::nc::oid(member); - }) - ); - if (filtered_members.size() != 1) - { - // can't find this oid, so member has been removed - return true; - } - const auto restore_member = *filtered_members.begin(); - // We ignore the description and user label as these are non-normative - if (nmos::fields::nc::role(reference_member) != nmos::fields::nc::role(restore_member) - || nmos::fields::nc::constant_oid(reference_member) != nmos::fields::nc::constant_oid(restore_member) - || nmos::fields::nc::class_id(reference_member) != nmos::fields::nc::class_id(restore_member) - || nmos::fields::nc::owner(reference_member) != nmos::fields::nc::owner(restore_member)) - { - return true; - } - } - } - - return false; - } - web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { using web::json::value; @@ -227,195 +127,6 @@ namespace nmos return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, bulk_values_holder); } - bool is_property_value_valid(const web::json::value& property_value, const web::json::value& property_descriptor, const web::json::value& restore_mode, web::json::value& property_restore_notices) - { - const nmos::nc_property_id& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)); - bool is_valid = true; - // Check the name of the property is correct - if (nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::name(property_value)) - { - utility::ostringstream_t os; - os << U("unexpected property name: expected ") << nmos::fields::nc::name(property_descriptor) << U(", actual ") << nmos::fields::nc::name(property_value); - 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::error, os.str()); - web::json::push_back(property_restore_notices, property_restore_notice); - is_valid = false; - } - // Check the type of the property value is correct - if (nmos::fields::nc::type_name(property_descriptor) != nmos::fields::nc::type_name(property_value)) - { - utility::ostringstream_t os; - os << U("unexpected property type: expected ") << nmos::fields::nc::type_name(property_descriptor) << U(", actual ") << nmos::fields::nc::type_name(property_value); - 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::error, os.str()); - web::json::push_back(property_restore_notices, property_restore_notice); - is_valid = false; - } - // Only allow modification of read only properties when in Rebuild mode - if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && restore_mode != nmos::nc_restore_mode::restore_mode::rebuild) - { - 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::error, U("read only properties can not be modified in Modify restore mode.")); - web::json::push_back(property_restore_notices, property_restore_notice); - is_valid = false; - } - return is_valid; - } - - web::json::value modify_device_model(nmos::resources& resources, const nmos::resource& resource, const web::json::value& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) - { - auto object_properties_set_validation_values = web::json::value::array(); - - // filter for the target_role_path and child objects - const auto& filtered_object_properties_holders = boost::copy_range>(object_properties_holders - | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) - { - return is_role_path_root(target_role_path, nmos::fields::nc::path(object_properties_holder)); - }) - ); - web::json::value child_object_properties_holders = web::json::value::array(); - for (const auto& filtered_holder : filtered_object_properties_holders) - { - web::json::push_back(child_object_properties_holders, filtered_holder); - } - - // get object_properties_holder for the target role path, if there is one - const auto& target_object_properties_holders = boost::copy_range>(object_properties_holders - | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) - { - return target_role_path == nmos::fields::nc::path(object_properties_holder); - }) - ); - // there should be 0 or 1 object_properties_holder for any role path. - if (target_object_properties_holders.size() > 1) - { - const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(nmos::fields::nc::path(*target_object_properties_holders.begin()), nmos::nc_restore_validation_status::failed, web::json::value::array(), U("more than one object_properties_holder for role path")); - web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); - return object_properties_set_validation_values; - } - - nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); - - if (nmos::is_nc_block(class_id)) - { - // if rebuildable and the block has changed then callback - if (nmos::fields::nc::is_rebuildable(resource.data) && target_object_properties_holders.size() && is_block_modified(resource, *target_object_properties_holders.begin())) - { - // call back to application code - return modify_rebuildable_block(resource, target_role_path, child_object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor); - } - // iterate through child objects - if (resource.data.has_field(nmos::fields::nc::members)) - { - const auto& members = nmos::fields::nc::members(resource.data); - - for (const auto& member : members) - { - const auto& child = find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(member)))); - - if (resources.end() != child) - { - // Apend the role of the child to the target role path to create the child role path - // Hmmmm, there must be a better way of appending the child role to the end of the target role path array... - auto child_role_path = web::json::value::array(); - for (const auto& path_element : target_role_path.as_array()) - { - web::json::push_back(child_role_path, path_element); - } - web::json::push_back(child_role_path, nmos::fields::nc::role(child->data)); - - web::json::value child_object_properties_set_validation_values = modify_device_model(resources, *child, child_role_path, child_object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); - // Hmmm, there must be a better way of marging two json array objects - for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) - { - web::json::push_back(object_properties_set_validation_values, validation_values); - } - } - } - } - } - for (const auto& target_object_properties_holder : target_object_properties_holders) - { - auto property_restore_notices = web::json::value::array(); - auto property_modify_list = web::json::value::array(); - auto read_only_property_modify_list = web::json::value::array(); - // Validate property_values - filter out the incorrect, ignored or unallowed - for (const auto& property_value : nmos::fields::nc::values(target_object_properties_holder)) - { - const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); - const auto& property_descriptor = nmos::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); - if(!is_property_value_valid(property_value, property_descriptor, restore_mode, property_restore_notices)) - { - continue; - } - // Ignore if no change is being requested - if (resource.data.at(nmos::fields::nc::name(property_descriptor)) == nmos::fields::nc::value(property_value)) - { - continue; - } - // Only allow modification of read only properties when in Rebuild mode - if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && restore_mode == nmos::nc_restore_mode::restore_mode::rebuild) - { - push_back(read_only_property_modify_list, property_value); - } - web::json::push_back(property_modify_list, property_value); - } - - if (filter_property_value_holders && read_only_property_modify_list.as_array().size() > 0) - { - // If this is a read only property then we should call back to the application code to - // check that it's OK to change this value. Bear in mind that this could be a class Id, or an oid or some other - // property that we don't want changed - property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list, recurse, restore_mode, validate, property_restore_notices, get_control_protocol_class_descriptor); - } - for (const auto& property_value : property_modify_list.as_array()) - { - const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); - - if (!validate) - { - // modify control protocol resources - const auto& value = nmos::fields::nc::value(property_value); - - modify_control_protocol_resource(resources, resource.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}})); - } - } - const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_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 object_properties_set_validation_values; - } - - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) - { - auto object_properties_set_validation_values = web::json::value::array(); - - const auto target_role_path = get_role_path(resources, resource); - - // Detect and warn if there are any object_properties_holders outside of the target role path's scope - const auto& orphan_object_properties_holders = boost::copy_range>(object_properties_holders - | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) - { - return !nmos::is_role_path_root(target_role_path, nmos::fields::nc::path(object_properties_holder)); - }) - ); - for (const auto& orphan_object_properties_holder : orphan_object_properties_holders) - { - const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(nmos::fields::nc::path(orphan_object_properties_holder), nmos::nc_restore_validation_status::not_found, web::json::value::array(), U("object role path not found under target role path")); - web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); - } - - web::json::value child_object_properties_set_validation_values = modify_device_model(resources, resource, target_role_path, object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); - for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) - { - web::json::push_back(object_properties_set_validation_values, validation_values); - } - - return object_properties_set_validation_values; - } - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) { // Do something with validation fingerprint? diff --git a/Development/nmos/configuration_methods.h b/Development/nmos/configuration_methods.h index 10f22ef34..c71b3e123 100644 --- a/Development/nmos/configuration_methods.h +++ b/Development/nmos/configuration_methods.h @@ -17,16 +17,6 @@ namespace nmos // Implementation of IS-14 function for creating backup dataset from a Device Model web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor); - // Check to see if role_path is sub path of parent_role_path - bool is_role_path_root(const web::json::value& role_path_, const web::json::value& parent_role_path); - - bool is_block_modified(const nmos::resource& resource, const web::json::value& object_properties_holder); - - // Get role path of resource given the Device Model resources - web::json::value get_role_path(const nmos::resources& resources, const nmos::resource& resource); - - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp new file mode 100644 index 000000000..96c369a0c --- /dev/null +++ b/Development/nmos/configuration_utils.cpp @@ -0,0 +1,306 @@ +#include "nmos/configuration_methods.h" + +#include +#include "cpprest/json_utils.h" +#include "nmos/configuration_handlers.h" +#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_resources.h" +#include "nmos/control_protocol_state.h" +#include "nmos/control_protocol_utils.h" +#include "nmos/slog.h" + +namespace nmos +{ + namespace details + { + bool is_property_value_valid(const web::json::value& property_value, const web::json::value& property_descriptor, const web::json::value& restore_mode, web::json::value& property_restore_notices) + { + const nmos::nc_property_id& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)); + bool is_valid = true; + // Check the name of the property is correct + if (nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::name(property_value)) + { + utility::ostringstream_t os; + os << U("unexpected property name: expected ") << nmos::fields::nc::name(property_descriptor) << U(", actual ") << nmos::fields::nc::name(property_value); + 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::error, os.str()); + web::json::push_back(property_restore_notices, property_restore_notice); + is_valid = false; + } + // Check the type of the property value is correct + if (nmos::fields::nc::type_name(property_descriptor) != nmos::fields::nc::type_name(property_value)) + { + utility::ostringstream_t os; + os << U("unexpected property type: expected ") << nmos::fields::nc::type_name(property_descriptor) << U(", actual ") << nmos::fields::nc::type_name(property_value); + 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::error, os.str()); + web::json::push_back(property_restore_notices, property_restore_notice); + is_valid = false; + } + // Only allow modification of read only properties when in Rebuild mode + if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && restore_mode != nmos::nc_restore_mode::restore_mode::rebuild) + { + 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::error, U("read only properties can not be modified in Modify restore mode.")); + web::json::push_back(property_restore_notices, property_restore_notice); + is_valid = false; + } + return is_valid; + } + } + + // Check to see if root_role_path is root of role_path_ + bool is_role_path_root(const web::json::value& role_path_root, const web::json::value& role_path_) + { + if (role_path_root.as_array().size() > role_path_.as_array().size()) + { + // root can't be longed that the path + return false; + } + for (size_t i = 0; i < role_path_root.as_array().size(); ++i) + { + if (role_path_root.as_array().at(i) != role_path_.as_array().at(i)) + { + return false; + } + } + return true; + } + + bool is_block_modified(const nmos::resource& resource, const web::json::value& object_properties_holder) + { + // Are they blocks? + nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + if (!nmos::is_nc_block(class_id)) + { + return false; + } + const auto& block_members_properties_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) + | boost::adaptors::filtered([](const web::json::value& property_value_holder) + { + return nmos::nc_property_id(2, 2) == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value_holder)); + }) + ); + // There should only be a single property holder for the members + if (block_members_properties_holders.size() == 1) + { + const auto& members_property_holder = *block_members_properties_holders.begin(); + const auto& restore_members = nmos::fields::nc::value(members_property_holder); + const auto& reference_members = nmos::fields::nc::members(resource.data); + + if (reference_members.size() != restore_members.as_array().size()) + { + return true; + } + for (const auto& reference_member : reference_members) + { + const auto& filtered_members = boost::copy_range>(restore_members.as_array() + | boost::adaptors::filtered([&reference_member](const web::json::value& member) + { + return nmos::fields::nc::oid(reference_member) == nmos::fields::nc::oid(member); + }) + ); + if (filtered_members.size() != 1) + { + // can't find this oid, so member has been removed + return true; + } + const auto restore_member = *filtered_members.begin(); + // We ignore the description and user label as these are non-normative + if (nmos::fields::nc::role(reference_member) != nmos::fields::nc::role(restore_member) + || nmos::fields::nc::constant_oid(reference_member) != nmos::fields::nc::constant_oid(restore_member) + || nmos::fields::nc::class_id(reference_member) != nmos::fields::nc::class_id(restore_member) + || nmos::fields::nc::owner(reference_member) != nmos::fields::nc::owner(restore_member)) + { + return true; + } + } + } + + return false; + } + + web::json::value modify_device_model(nmos::resources& resources, const nmos::resource& resource, const web::json::value& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + { + auto object_properties_set_validation_values = web::json::value::array(); + + // filter for the target_role_path and child objects + const auto& filtered_object_properties_holders = boost::copy_range>(object_properties_holders + | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) + { + return is_role_path_root(target_role_path, nmos::fields::nc::path(object_properties_holder)); + }) + ); + web::json::value child_object_properties_holders = web::json::value::array(); + for (const auto& filtered_holder : filtered_object_properties_holders) + { + web::json::push_back(child_object_properties_holders, filtered_holder); + } + + // get object_properties_holder for the target role path, if there is one + const auto& target_object_properties_holders = boost::copy_range>(object_properties_holders + | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) + { + return target_role_path == nmos::fields::nc::path(object_properties_holder); + }) + ); + // there should be 0 or 1 object_properties_holder for any role path. + if (target_object_properties_holders.size() > 1) + { + const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(nmos::fields::nc::path(*target_object_properties_holders.begin()), nmos::nc_restore_validation_status::failed, web::json::value::array(), U("more than one object_properties_holder for role path")); + web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + return object_properties_set_validation_values; + } + + nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + + if (nmos::is_nc_block(class_id)) + { + // if rebuildable and the block has changed then callback + if (nmos::fields::nc::is_rebuildable(resource.data) && target_object_properties_holders.size() && is_block_modified(resource, *target_object_properties_holders.begin())) + { + // call back to application code + return modify_rebuildable_block(resource, target_role_path, child_object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor); + } + // iterate through child objects + if (resource.data.has_field(nmos::fields::nc::members)) + { + const auto& members = nmos::fields::nc::members(resource.data); + + for (const auto& member : members) + { + const auto& child = find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(member)))); + + if (resources.end() != child) + { + // Apend the role of the child to the target role path to create the child role path + // Hmmmm, there must be a better way of appending the child role to the end of the target role path array... + auto child_role_path = web::json::value::array(); + for (const auto& path_element : target_role_path.as_array()) + { + web::json::push_back(child_role_path, path_element); + } + web::json::push_back(child_role_path, nmos::fields::nc::role(child->data)); + + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, *child, child_role_path, child_object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + // Hmmm, there must be a better way of marging two json array objects + for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) + { + web::json::push_back(object_properties_set_validation_values, validation_values); + } + } + } + } + } + for (const auto& target_object_properties_holder : target_object_properties_holders) + { + auto property_restore_notices = web::json::value::array(); + auto property_modify_list = web::json::value::array(); + auto read_only_property_modify_list = web::json::value::array(); + // Validate property_values - filter out the incorrect, ignored or unallowed + for (const auto& property_value : nmos::fields::nc::values(target_object_properties_holder)) + { + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_descriptor = nmos::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); + if (!details::is_property_value_valid(property_value, property_descriptor, restore_mode, property_restore_notices)) + { + continue; + } + // Ignore if no change is being requested + if (resource.data.at(nmos::fields::nc::name(property_descriptor)) == nmos::fields::nc::value(property_value)) + { + continue; + } + // Only allow modification of read only properties when in Rebuild mode + if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && restore_mode == nmos::nc_restore_mode::restore_mode::rebuild) + { + push_back(read_only_property_modify_list, property_value); + } + web::json::push_back(property_modify_list, property_value); + } + + if (filter_property_value_holders && read_only_property_modify_list.as_array().size() > 0) + { + // If this is a read only property then we should call back to the application code to + // check that it's OK to change this value. Bear in mind that this could be a class Id, or an oid or some other + // property that we don't want changed + property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list, recurse, restore_mode, validate, property_restore_notices, get_control_protocol_class_descriptor); + } + for (const auto& property_value : property_modify_list.as_array()) + { + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + + if (!validate) + { + // modify control protocol resources + const auto& value = nmos::fields::nc::value(property_value); + + modify_control_protocol_resource(resources, resource.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} })); + } + } + const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_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 object_properties_set_validation_values; + } + + 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 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()) + { + 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); + web::json::push_back(role_path, nmos::fields::nc::role(found_resource.data)); + oid = nmos::fields::nc::id(found_resource.data); + } + + std::reverse(role_path.as_array().begin(), role_path.as_array().end()); + + return role_path; + } + + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + { + auto object_properties_set_validation_values = web::json::value::array(); + + const auto target_role_path = get_role_path(resources, resource); + + // Detect and warn if there are any object_properties_holders outside of the target role path's scope + const auto& orphan_object_properties_holders = boost::copy_range>(object_properties_holders + | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) + { + return !nmos::is_role_path_root(target_role_path, nmos::fields::nc::path(object_properties_holder)); + }) + ); + for (const auto& orphan_object_properties_holder : orphan_object_properties_holders) + { + const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(nmos::fields::nc::path(orphan_object_properties_holder), nmos::nc_restore_validation_status::not_found, web::json::value::array(), U("object role path not found under target role path")); + web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + } + + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, resource, target_role_path, object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) + { + web::json::push_back(object_properties_set_validation_values, validation_values); + } + + return object_properties_set_validation_values; + } +} \ No newline at end of file diff --git a/Development/nmos/configuration_utils.h b/Development/nmos/configuration_utils.h new file mode 100644 index 000000000..cd6132538 --- /dev/null +++ b/Development/nmos/configuration_utils.h @@ -0,0 +1,23 @@ +#ifndef NMOS_CONFIGURATION_UTILS_H +#define NMOS_CONFIGURATION_UTILS_H + +#include "nmos/configuration_handlers.h" +#include "nmos/control_protocol_handlers.h" +#include "nmos/resources.h" + +namespace nmos +{ + struct control_protocol_resource; + + // Check to see if role_path is sub path of parent_role_path + bool is_role_path_root(const web::json::value& role_path_, const web::json::value& parent_role_path); + + bool is_block_modified(const nmos::resource& resource, const web::json::value& object_properties_holder); + + // Get role path of resource given the Device Model resources + web::json::value get_role_path(const nmos::resources& resources, const nmos::resource& resource); + + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); +} + +#endif \ No newline at end of file diff --git a/Development/nmos/test/configuration_methods_test.cpp b/Development/nmos/test/configuration_utils_test.cpp similarity index 99% rename from Development/nmos/test/configuration_methods_test.cpp rename to Development/nmos/test/configuration_utils_test.cpp index 77692a5b6..7f288a996 100644 --- a/Development/nmos/test/configuration_methods_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -5,7 +5,7 @@ #include "nmos/control_protocol_typedefs.h" #include "nmos/control_protocol_utils.h" #include "nmos/configuration_handlers.h" -#include "nmos/configuration_methods.h" +#include "nmos/configuration_utils.h" #include "bst/test/test.h" From b960e13ffbab800263620d463dc346f4fefb4a1a Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Thu, 2 Jan 2025 17:01:02 +0000 Subject: [PATCH 149/250] Add get_role_path tests --- .../nmos/test/configuration_utils_test.cpp | 55 ++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 7f288a996..033a0e362 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -225,6 +225,57 @@ BST_TEST_CASE(testIsBlockModified) } +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testGetRolePath) +{ + // Create Fake Device Model + using web::json::value_of; + using web::json::value; + + nmos::experimental::control_protocol_state control_protocol_state; + nmos::resources resources; + + // root + auto root_block = nmos::make_root_block(); + nmos::nc_oid oid = nmos::root_block_oid; + // root, ClassManager + auto class_manager = nmos::make_class_manager(++oid, control_protocol_state); + nmos::nc_oid receiver_block_oid = ++oid; + // root, receivers + auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block"), web::json::value::null(), web::json::value::null(), web::json::value::array(), true); + // root, receivers, mon1 + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + nmos::nc_class_id monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + // root, receivers, mon2 + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + nmos::push_back(receivers, monitor1); + // add example-control to root-block + nmos::push_back(receivers, monitor2); + // add stereo-gain to root-block + nmos::push_back(root_block, receivers); + // add class-manager to root-block + nmos::push_back(root_block, class_manager); + insert_resource(resources, std::move(root_block)); + insert_resource(resources, std::move(class_manager)); + insert_resource(resources, std::move(receivers)); + insert_resource(resources, std::move(monitor1)); + insert_resource(resources, std::move(monitor2)); + + value expected_role_paths = value::array(); + push_back(expected_role_paths, value_of({ U("root") })); + push_back(expected_role_paths, value_of({ U("root"), U("ClassManager")})); + push_back(expected_role_paths, value_of({ U("root"), U("receivers") })); + push_back(expected_role_paths, value_of({ U("root"), U("receivers"), U("mon1") })); + push_back(expected_role_paths, value_of({ U("root"), U("receivers"), U("mon2") })); + + for (const auto& expected_role_path : expected_role_paths.as_array()) + { + const auto& resource = find_control_protocol_resource_by_role_path(resources, expected_role_path); + value actual_role_path = nmos::get_role_path(resources, *resource); + BST_CHECK_EQUAL(expected_role_path, actual_role_path); + } +} + //////////////////////////////////////////////////////////////////////////////////////////// BST_TEST_CASE(testApplyBackupDataSet) { @@ -239,11 +290,11 @@ BST_TEST_CASE(testApplyBackupDataSet) // root auto root_block = nmos::make_root_block(); nmos::nc_oid oid = nmos::root_block_oid; - // root, Class Date: Fri, 3 Jan 2025 15:13:37 +0000 Subject: [PATCH 150/250] Handle undefined callback functions --- Development/nmos/configuration_utils.cpp | 32 +++- .../nmos/test/configuration_utils_test.cpp | 157 +++++++++++++++++- 2 files changed, 180 insertions(+), 9 deletions(-) diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 96c369a0c..b6a0573dd 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -156,8 +156,17 @@ namespace nmos // if rebuildable and the block has changed then callback if (nmos::fields::nc::is_rebuildable(resource.data) && target_object_properties_holders.size() && is_block_modified(resource, *target_object_properties_holders.begin())) { - // call back to application code - return modify_rebuildable_block(resource, target_role_path, child_object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor); + if (modify_rebuildable_block) + { + // call back to application code which will return an object_properties_set_validation_values object + return modify_rebuildable_block(resource, target_role_path, child_object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor); + } + else + { + const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, web::json::value::array(), U("Rebuilding of Device Model blocks not supported")); + web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + return object_properties_set_validation_values; + } } // iterate through child objects if (resource.data.has_field(nmos::fields::nc::members)) @@ -216,12 +225,21 @@ namespace nmos web::json::push_back(property_modify_list, property_value); } - if (filter_property_value_holders && read_only_property_modify_list.as_array().size() > 0) + if (read_only_property_modify_list.as_array().size() > 0) { - // If this is a read only property then we should call back to the application code to - // check that it's OK to change this value. Bear in mind that this could be a class Id, or an oid or some other - // property that we don't want changed - property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list, recurse, restore_mode, validate, property_restore_notices, get_control_protocol_class_descriptor); + if (filter_property_value_holders) + { + // If this is a read only property then we should call back to the application code to + // check that it's OK to change this value. Bear in mind that this could be a class Id, or an oid or some other + // property that we don't want changed + property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list, recurse, restore_mode, validate, property_restore_notices, get_control_protocol_class_descriptor); + } + else + { + const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, property_restore_notices, U("Modification of read only properties not supported")); + web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + continue; + } } for (const auto& property_value : property_modify_list.as_array()) { diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 033a0e362..7e1f8da97 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -221,8 +221,6 @@ BST_TEST_CASE(testIsBlockModified) BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } - - } //////////////////////////////////////////////////////////////////////////////////////////// @@ -659,4 +657,159 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(!modify_rebuildable_block_called); } // ensure an error if trying to invoke rebuildable block when in Modify mode +} + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) +{ + using web::json::value_of; + using web::json::value; + + nmos::resources resources; + nmos::experimental::control_protocol_state control_protocol_state; + nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor = nmos::make_get_control_protocol_class_descriptor_handler(control_protocol_state); + + // Create Device Model + // root + auto root_block = nmos::make_root_block(); + nmos::nc_oid oid = nmos::root_block_oid; + // root, ClassManager + auto class_manager = nmos::make_class_manager(++oid, control_protocol_state); + nmos::nc_oid receiver_block_oid = ++oid; + // root, receivers + auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block"), web::json::value::null(), web::json::value::null(), web::json::value::array(), true); + // root, receivers, mon1 + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + nmos::nc_oid monitor_1_oid = oid; + nmos::nc_class_id monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + // root, receivers, mon2 + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + nmos::push_back(receivers, monitor1); + // add example-control to root-block + nmos::push_back(receivers, monitor2); + // add stereo-gain to root-block + nmos::push_back(root_block, receivers); + // add class-manager to root-block + nmos::push_back(root_block, class_manager); + insert_resource(resources, std::move(root_block)); + insert_resource(resources, std::move(class_manager)); + insert_resource(resources, std::move(receivers)); + insert_resource(resources, std::move(monitor1)); + insert_resource(resources, std::move(monitor2)); + + // undefined callback stubs + nmos::filter_property_value_holders_handler filter_property_value_holders; + nmos::modify_rebuildable_block_handler modify_rebuildable_block; + + { + // Check that Modify mode is unaffected by undefined Rebuild mode callbacks + // + // Create Object Properties Holder + value object_properties_holders = value::array(); + value role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + value property_value_holders = value::array(); + value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false); + push_back(property_value_holders, property_value_holder); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + push_back(object_properties_holders, object_properties_holder); + value target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + bool validate = true; + web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::modify; + + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + value object_properties_set_validation = output.as_array().at(0); + + BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + } + { + // Check that Rebuild mode is unaffected by undefined Rebuild mode callbacks when no objects are being rebuilt + // + // Create Object Properties Holder + value object_properties_holders = value::array(); + value role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + value property_value_holders = value::array(); + value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false); + push_back(property_value_holders, property_value_holder); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + push_back(object_properties_holders, object_properties_holder); + value target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + bool validate = true; + web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; + + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + value object_properties_set_validation = output.as_array().at(0); + + BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + } + { + // Check undefined filter_property_value_holders_handler causes an unsupported mode error when attempting to modify a read only property in Rebuild mode + // + // Create Object Properties Holder + value object_properties_holders = value::array(); + value role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + value property_value_holders = value::array(); + nmos::nc_property_id property_id(2, 1); + // This is a read only property + value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); + push_back(property_value_holders, property_value_holder); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + push_back(object_properties_holders, object_properties_holder); + // must be a more efficient way of initializing these role paths + value target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; + bool validate = true; + + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + + value object_properties_set_validation = output.as_array().at(0); + + // make sure the validation status propagates from the callback + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); + } + { + // Check undefined modify_rebuildable_block_handler causes an unsupported error when attempting to modify a rebuildable block + // + // Create Object Properties Holder + value object_properties_holders = value::array(); + value role_path = value_of({ U("root"), U("receivers") }); + value property_value_holders = value::array(); + value members = value::array(); + + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false)); + value target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + bool validate = true; + web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; + + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + value object_properties_set_validation = output.as_array().at(0); + + BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); + } } \ No newline at end of file From 4e194f70f401159320b3af9932e82af5f6a519a6 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 6 Jan 2025 13:41:29 +0000 Subject: [PATCH 151/250] Change error message --- Development/nmos-cpp-node/node_implementation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 3da8366b8..4963a7056 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1743,7 +1743,7 @@ nmos::filter_property_value_holders_handler make_filter_property_value_holders_h if (bool(nmos::fields::nc::is_read_only(property_descriptor))) { // We need to create a notice for any properties that will not be updated - 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("can not update read only properties")); + 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("Update of read only properties not supported")); web::json::push_back(property_restore_notices, property_restore_notice); } else From 17683cab999d4ae64ff5c54e710eb962422f2e62 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 6 Jan 2025 13:41:53 +0000 Subject: [PATCH 152/250] refactor modify_device_model --- Development/nmos/configuration_utils.cpp | 72 +++++++++++++++--------- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index b6a0573dd..8a449b73e 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -44,6 +44,21 @@ namespace nmos } return is_valid; } + + bool is_contains_read_only_property(const web::json::array& property_values, const nmos::nc_class_id& class_id, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + { + for (const auto& property_value : property_values) + { + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_descriptor = nmos::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); + + if (bool(nmos::fields::nc::is_read_only(property_descriptor))) + { + return true; + } + } + return false; + } } // Check to see if root_role_path is root of role_path_ @@ -121,7 +136,10 @@ namespace nmos { auto object_properties_set_validation_values = web::json::value::array(); - // filter for the target_role_path and child objects + // Filter for the target_role_path and child objects + // + // hmmmmm, I don't like this two step filter process - creating a boost array and then converting to a json array. + // Could this be done in a single step? const auto& filtered_object_properties_holders = boost::copy_range>(object_properties_holders | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) { @@ -144,12 +162,13 @@ namespace nmos // there should be 0 or 1 object_properties_holder for any role path. if (target_object_properties_holders.size() > 1) { + // Error in the backup dataset const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(nmos::fields::nc::path(*target_object_properties_holders.begin()), nmos::nc_restore_validation_status::failed, web::json::value::array(), U("more than one object_properties_holder for role path")); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); return object_properties_set_validation_values; } - nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); if (nmos::is_nc_block(class_id)) { @@ -163,6 +182,7 @@ namespace nmos } else { + // Rebuilding blocks not supported const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, web::json::value::array(), U("Rebuilding of Device Model blocks not supported")); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); return object_properties_set_validation_values; @@ -179,7 +199,7 @@ namespace nmos if (resources.end() != child) { - // Apend the role of the child to the target role path to create the child role path + // Append the role of the child to the target role path to create the child role path // Hmmmm, there must be a better way of appending the child role to the end of the target role path array... auto child_role_path = web::json::value::array(); for (const auto& path_element : target_role_path.as_array()) @@ -189,7 +209,7 @@ namespace nmos web::json::push_back(child_role_path, nmos::fields::nc::role(child->data)); web::json::value child_object_properties_set_validation_values = modify_device_model(resources, *child, child_role_path, child_object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); - // Hmmm, there must be a better way of marging two json array objects + // Hmmm, there must be a better way of merging two json array objects for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) { web::json::push_back(object_properties_set_validation_values, validation_values); @@ -201,41 +221,37 @@ namespace nmos for (const auto& target_object_properties_holder : target_object_properties_holders) { auto property_restore_notices = web::json::value::array(); + + // Validate property_values - filter out the incorrect, ignored or unallowed values + // Hmm as above, don't like the two step filter process here + const auto& filtered_property_values = boost::copy_range>(nmos::fields::nc::values(target_object_properties_holder) + | boost::adaptors::filtered([&property_restore_notices, &resource, class_id, get_control_protocol_class_descriptor, restore_mode](const web::json::value& property_value) + { + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_descriptor = nmos::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); + + return details::is_property_value_valid(property_value, property_descriptor, restore_mode, property_restore_notices) + && resource.data.at(nmos::fields::nc::name(property_descriptor)) != nmos::fields::nc::value(property_value); + }) + ); auto property_modify_list = web::json::value::array(); - auto read_only_property_modify_list = web::json::value::array(); - // Validate property_values - filter out the incorrect, ignored or unallowed - for (const auto& property_value : nmos::fields::nc::values(target_object_properties_holder)) + for (const auto& property_value : filtered_property_values) { - const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); - const auto& property_descriptor = nmos::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); - if (!details::is_property_value_valid(property_value, property_descriptor, restore_mode, property_restore_notices)) - { - continue; - } - // Ignore if no change is being requested - if (resource.data.at(nmos::fields::nc::name(property_descriptor)) == nmos::fields::nc::value(property_value)) - { - continue; - } - // Only allow modification of read only properties when in Rebuild mode - if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && restore_mode == nmos::nc_restore_mode::restore_mode::rebuild) - { - push_back(read_only_property_modify_list, property_value); - } web::json::push_back(property_modify_list, property_value); } - if (read_only_property_modify_list.as_array().size() > 0) + if (details::is_contains_read_only_property(property_modify_list.as_array(), class_id, get_control_protocol_class_descriptor)) { if (filter_property_value_holders) { - // If this is a read only property then we should call back to the application code to - // check that it's OK to change this value. Bear in mind that this could be a class Id, or an oid or some other + // If the property_modify_list contains read only properties then we call back to the application code to + // check that it's OK to change those value. Bear in mind that they could be the class Id, or the oid or some other // property that we don't want changed property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list, recurse, restore_mode, validate, property_restore_notices, get_control_protocol_class_descriptor); } else { + // Modify of read only properties not supported const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, property_restore_notices, U("Modification of read only properties not supported")); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); continue; @@ -245,6 +261,8 @@ namespace nmos { const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + // hmmm, ideally we would pass the value into modify_control_protocol_resource with the validate + // flag, so that it's subject to property contraints and also the application code can decide if it's a legal value if (!validate) { // modify control protocol resources @@ -301,6 +319,7 @@ namespace nmos const auto target_role_path = get_role_path(resources, resource); // Detect and warn if there are any object_properties_holders outside of the target role path's scope + // Hmmm, can this be done as a one step process rather than filtering and then iterating over filtered list? const auto& orphan_object_properties_holders = boost::copy_range>(object_properties_holders | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) { @@ -314,6 +333,7 @@ namespace nmos } web::json::value child_object_properties_set_validation_values = modify_device_model(resources, resource, target_role_path, object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + // Hmmm - there must be a better way to append an array for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) { web::json::push_back(object_properties_set_validation_values, validation_values); From d282bf042a743cbeaada01022fc6f3902614f4e7 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 8 Jan 2025 16:38:51 +0000 Subject: [PATCH 153/250] Indicate rebuildable control protocol resources --- .../nmos/control_protocol_resource.cpp | 9 +++++---- Development/nmos/control_protocol_resource.h | 4 ++-- .../nmos/control_protocol_resources.cpp | 19 ++++++++++++++----- Development/nmos/control_protocol_resources.h | 5 ++++- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 72c06883c..393bbfb6d 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -697,7 +697,7 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject - web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool is_rebuildable) + web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { using web::json::value; @@ -713,16 +713,17 @@ namespace nmos data[nmos::fields::nc::runtime_property_constraints] = runtime_property_constraints; // level 2 runtime constraints. See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html // IS-14 isRebuilable flag - data[nmos::fields::nc::is_rebuildable] = value::boolean(is_rebuildable); + // use make_rebuildable function to declare an control protocl resource rebuildable + data[nmos::fields::nc::is_rebuildable] = value::boolean(false); return data; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock - web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members, bool is_rebuildable) + web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members) { using web::json::value; - auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints, is_rebuildable); + auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); data[nmos::fields::nc::enabled] = value::boolean(enabled); data[nmos::fields::nc::members] = members; diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 41ad95b5e..72365d583 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -169,10 +169,10 @@ namespace nmos web::json::value make_nc_touchpoint_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject - web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool is_rebuildable=false); + web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock - web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members, bool is_rebuildable); + web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled); diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index 67d68669f..69c782a0f 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -9,22 +9,31 @@ namespace nmos namespace details { // create block resource - control_protocol_resource make_block(nmos::nc_oid oid, const web::json::value& owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members, bool is_rebuildable) + control_protocol_resource make_block(nmos::nc_oid oid, const web::json::value& owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) { using web::json::value; - auto data = details::make_nc_block(nc_block_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true, members, is_rebuildable); + auto data = details::make_nc_block(nc_block_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true, members); return{ is12_versions::v1_0, types::nc_block, std::move(data), true }; } } // create block resource - control_protocol_resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members, bool is_rebuildable) + control_protocol_resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& members) { using web::json::value; - return details::make_block(oid, value(owner), role, user_label, description, touchpoints, runtime_property_constraints, members, is_rebuildable); + return details::make_block(oid, value(owner), role, user_label, description, touchpoints, runtime_property_constraints, members); + } + + control_protocol_resource make_rebuildable(control_protocol_resource& control_protocol_resource) + { + using web::json::value; + + control_protocol_resource.data[nmos::fields::nc::is_rebuildable] = value::boolean(true); + + return control_protocol_resource; } // create Root block resource @@ -32,7 +41,7 @@ namespace nmos { using web::json::value; - return details::make_block(nmos::root_block_oid, value::null(), nmos::root_block_role, U("Root"), U("Root block"), value::null(), value::null(), value::array(), false); + return details::make_block(nmos::root_block_oid, value::null(), nmos::root_block_role, U("Root"), U("Root block"), value::null(), value::null(), value::array()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager diff --git a/Development/nmos/control_protocol_resources.h b/Development/nmos/control_protocol_resources.h index 13337d39b..25526cca9 100644 --- a/Development/nmos/control_protocol_resources.h +++ b/Development/nmos/control_protocol_resources.h @@ -14,7 +14,10 @@ namespace nmos struct control_protocol_resource; // create block resource - control_protocol_resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), const web::json::value& members = web::json::value::array(), bool is_rebuildable=false); + control_protocol_resource make_block(nc_oid oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints = web::json::value::null(), const web::json::value& runtime_property_constraints = web::json::value::null(), const web::json::value& members = web::json::value::array()); + + // make object rebuildable - for IS-14 dynamic configuration of Device Model + control_protocol_resource make_rebuildable(control_protocol_resource& control_protocol_resource); // create Root block resource control_protocol_resource make_root_block(); From 21bb8a965a654cb06163e457242d6f732374d616 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 8 Jan 2025 16:39:28 +0000 Subject: [PATCH 154/250] Test configuration utils --- Development/nmos/configuration_utils.cpp | 18 +++-- .../nmos/test/configuration_utils_test.cpp | 66 +++++++++++++++++-- 2 files changed, 74 insertions(+), 10 deletions(-) diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 8a449b73e..76efa1336 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -13,7 +13,7 @@ namespace nmos { namespace details { - bool is_property_value_valid(const web::json::value& property_value, const web::json::value& property_descriptor, const web::json::value& restore_mode, web::json::value& property_restore_notices) + bool is_property_value_valid(web::json::value& property_restore_notices, const web::json::value& property_value, const web::json::value& property_descriptor, const web::json::value& restore_mode, bool is_rebuildable) { const nmos::nc_property_id& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)); bool is_valid = true; @@ -36,12 +36,22 @@ namespace nmos is_valid = false; } // Only allow modification of read only properties when in Rebuild mode - if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && restore_mode != nmos::nc_restore_mode::restore_mode::rebuild) + if (bool(nmos::fields::nc::is_read_only(property_descriptor)) + && restore_mode != nmos::nc_restore_mode::restore_mode::rebuild) { 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::error, U("read only properties can not be modified in Modify restore mode.")); web::json::push_back(property_restore_notices, property_restore_notice); is_valid = false; } + // Only allow modification of read only properties when object is rebuildable + if (bool(nmos::fields::nc::is_read_only(property_descriptor)) + && !is_rebuildable) + { + 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::error, U("object must be rebuildable to allow modification of read only properties.")); + web::json::push_back(property_restore_notices, property_restore_notice); + is_valid = false; + } + return is_valid; } @@ -230,8 +240,8 @@ namespace nmos const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); const auto& property_descriptor = nmos::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); - return details::is_property_value_valid(property_value, property_descriptor, restore_mode, property_restore_notices) - && resource.data.at(nmos::fields::nc::name(property_descriptor)) != nmos::fields::nc::value(property_value); + return resource.data.at(nmos::fields::nc::name(property_descriptor)) != nmos::fields::nc::value(property_value) + && details::is_property_value_valid(property_restore_notices, property_value, property_descriptor, restore_mode, bool(nmos::fields::nc::is_rebuildable(resource.data))); }) ); auto property_modify_list = web::json::value::array(); diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 7e1f8da97..1dc2a06fb 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -240,7 +240,8 @@ BST_TEST_CASE(testGetRolePath) auto class_manager = nmos::make_class_manager(++oid, control_protocol_state); nmos::nc_oid receiver_block_oid = ++oid; // root, receivers - auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block"), web::json::value::null(), web::json::value::null(), web::json::value::array(), true); + auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); + nmos::make_rebuildable(receivers); // root, receivers, mon1 auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); nmos::nc_class_id monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); @@ -292,9 +293,13 @@ BST_TEST_CASE(testApplyBackupDataSet) auto class_manager = nmos::make_class_manager(++oid, control_protocol_state); nmos::nc_oid receiver_block_oid = ++oid; // root, receivers - auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block"), web::json::value::null(), web::json::value::null(), web::json::value::array(), true); + auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); + nmos::make_rebuildable(receivers); // root, receivers, mon1 auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({{nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); + // make monitor1 rebuildable + nmos::make_rebuildable(monitor1); + nmos::nc_oid monitor_1_oid = oid; nmos::nc_class_id monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 @@ -343,7 +348,7 @@ BST_TEST_CASE(testApplyBackupDataSet) value object_properties_holders = value::array(); value role_path = value_of({ U("root"), U("receivers"), U("mon1") }); value property_value_holders = value::array(); - value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false); + value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); push_back(property_value_holders, property_value_holder); auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); push_back(object_properties_holders, object_properties_holder); @@ -368,7 +373,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(!modify_rebuildable_block_called); } { - // Check filter_property_value_holders_handler is called when changing a read only property in Rebuild mode + // Check filter_property_value_holders_handler is called when changing a read only property of rebuildable object in Rebuild mode // filter_property_value_holders_called = false; modify_rebuildable_block_called = false; @@ -405,6 +410,54 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(filter_property_value_holders_called); BST_CHECK(!modify_rebuildable_block_called); } + { + // Check error generated when attempting to change a read only property of non-rebuidable object in Rebuild mode + // + filter_property_value_holders_called = false; + modify_rebuildable_block_called = false; + + // Create Object Properties Holder + value object_properties_holders = value::array(); + value role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + value property_value_holders = value::array(); + nmos::nc_property_id property_id(2, 1); + // This is a read only property + value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); + push_back(property_value_holders, property_value_holder); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + push_back(object_properties_holders, object_properties_holder); + // must be a more efficient way of initializing these role paths + value target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; + bool validate = true; + + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); + value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + + value object_properties_set_validation = output.as_array().at(0); + + // make sure the validation status propagates from the callback + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + + const auto& property_restore_notices = nmos::fields::nc::notices(object_properties_set_validation); + // expectation a single notice for the read only property that couldn't be changed + BST_REQUIRE_EQUAL(1, property_restore_notices.size()); + + const auto& notice = *property_restore_notices.begin(); + BST_CHECK_EQUAL(nmos::nc_property_id(3, 2), nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); + BST_CHECK_EQUAL(U("connectionStatusMessage"), nmos::fields::nc::name(notice)); + BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); + BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); + + // expecting callback to filter_property_value_holders_called + // but not to modify_rebuildable_block_called + BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!modify_rebuildable_block_called); + } { // Check an error is caused by trying to modify a read only property in Modify mode // @@ -445,7 +498,7 @@ BST_TEST_CASE(testApplyBackupDataSet) // expectation a single notice for the read only property that couldn't be changed BST_REQUIRE_EQUAL(1, property_restore_notices.size()); - const auto notice = *property_restore_notices.begin(); + const auto& notice = *property_restore_notices.begin(); BST_CHECK_EQUAL(nmos::nc_property_id(3, 2), nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); BST_CHECK_EQUAL(U("connectionStatusMessage"), nmos::fields::nc::name(notice)); BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); @@ -677,7 +730,8 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto class_manager = nmos::make_class_manager(++oid, control_protocol_state); nmos::nc_oid receiver_block_oid = ++oid; // root, receivers - auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block"), web::json::value::null(), web::json::value::null(), web::json::value::array(), true); + auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); + nmos::make_rebuildable(receivers); // root, receivers, mon1 auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); nmos::nc_oid monitor_1_oid = oid; From 64e4468fef487591efd187a8a39421270de8f585 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 8 Jan 2025 16:40:03 +0000 Subject: [PATCH 155/250] Example rebuildable block handler --- .../nmos-cpp-node/node_implementation.cpp | 169 +++++++++++++++++- 1 file changed, 163 insertions(+), 6 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 4963a7056..9ef6051fd 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1257,6 +1257,10 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr { make_example_datatype(example_enum::Alpha, U("example"), 50, false), make_example_datatype(example_enum::Gamma, U("different"), 75, true) } ); + const auto receiver_block_oid = ++oid; + auto receiver_block = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receiver Monitors"), U("Receiver Monitors")); + nmos::make_rebuildable(receiver_block); + // example receiver-monitor(s) { int count = 0; @@ -1269,10 +1273,10 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr utility::ostringstream_t role; role << U("monitor-") << ++count; const auto& receiver = nmos::find_resource(model.node_resources, receiver_id); - const auto receiver_monitor = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, receiver_id}) } })); + const auto receiver_monitor = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, receiver_id}) } })); // add receiver-monitor to root-block - nmos::push_back(root_block, receiver_monitor); + nmos::push_back(receiver_block, receiver_monitor); } } } @@ -1280,6 +1284,8 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // example temperature-sensor const auto temperature_sensor = make_temperature_sensor(++oid, nmos::root_block_oid, U("temperature-sensor"), U("Temperature Sensor"), U("Temperature Sensor block"), value::null(), value::null(), 0.0, U("Celsius")); + // add receiver monitor block + nmos::push_back(root_block, receiver_block); // add temperature-sensor to root-block nmos::push_back(root_block, temperature_sensor); // add example-control to root-block @@ -1757,13 +1763,164 @@ nmos::filter_property_value_holders_handler make_filter_property_value_holders_h } // Example Device Configuration callback for restoring a back-up dataset -nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmos::resources& resources, slog::base_gate& gate) +nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmos::node_model& model, slog::base_gate& gate) { - return [&resources, &gate](const nmos::resource& resource, const web::json::value& target_role_path, const web::json::value& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + return [&model, &gate](const nmos::resource& resource, const web::json::value& target_role_path, const web::json::value& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { + nmos::resources& resources = model.control_protocol_resources; + slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do modify_rebuildable_block"; - return web::json::value(); + if (object_properties_holders.size() != 1) + { + // Error + return web::json::value::array(); + } + const auto& object_properties_holder = *object_properties_holders.as_array().begin(); + + const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + if (!nmos::is_nc_block(class_id)) + { + // Error + return web::json::value::array(); + } + const auto& block_members_properties_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) + | boost::adaptors::filtered([](const web::json::value& property_value_holder) + { + return nmos::nc_property_id(2, 2) == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value_holder)); + }) + ); + // There should only be a single property holder for the members + if (block_members_properties_holders.size() != 1) + { + // Error + return web::json::value::array(); + } + + const auto& members_property_holder = *block_members_properties_holders.begin(); + const auto& restore_members = nmos::fields::nc::value(members_property_holder); + const auto& reference_members = nmos::fields::nc::members(resource.data); + + std::vector members_to_remove; + + for (const auto& reference_member : reference_members) + { + const auto& filtered_members = boost::copy_range>(restore_members.as_array() + | boost::adaptors::filtered([&reference_member](const web::json::value& member) + { + return nmos::fields::nc::oid(reference_member) == nmos::fields::nc::oid(member); + }) + ); + if (filtered_members.size() != 1) + { + // can't find this oid in restore dataset, so member has been removed + // Remove this resource + auto found = nmos::find_resource_if(resources, nmos::types::nc_receiver_monitor, [&reference_member](const nmos::resource& resource) + { + return nmos::fields::nc::oid(reference_member) == nmos::fields::nc::oid(resource.data); + }); + +// const auto& receiver_monitor = *(reference_members.begin()); + const auto& touchpoints = found->data.at(nmos::fields::nc::touchpoints); + + if (touchpoints.size() == 0) + { + // Error + continue; + } + + const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); + const auto& receiver = nmos::find_resource(resources, touchpoint_uuid.as_string()); + { + const auto& lock = model.write_lock(); + // remove receiver + bool success = erase_resource(model.node_resources, nmos::fields::id(receiver->data)); + + if (!success) + { + // Error + continue; + } + const auto oid = found->id; + utility::ostringstream_t id_str; + id_str << oid; + success = erase_resource(resources, oid); + if (success) + { + members_to_remove.push_back(oid); + } + } + } + const auto restore_member = *filtered_members.begin(); + // We ignore the description and user label as these are non-normative + if (nmos::fields::nc::role(reference_member) != nmos::fields::nc::role(restore_member) + || nmos::fields::nc::constant_oid(reference_member) != nmos::fields::nc::constant_oid(restore_member) + || nmos::fields::nc::class_id(reference_member) != nmos::fields::nc::class_id(restore_member) + || nmos::fields::nc::owner(reference_member) != nmos::fields::nc::owner(restore_member)) + { + // Modify existing resource + // in this example we will ignore/reject changes to the role, constant_oid, class_id or owner + // Do nothing, return warning + continue; + } + } + auto modified_members = web::json::value::array(); + + for (const auto& member : reference_members) + { + const auto& remove_member = boost::copy_range>(members_to_remove | boost::adaptors::filtered([&member](const nmos::id& oid) + { + return oid == nmos::fields::id(member); + }) + ); + + if (remove_member.size() == 0) + { + web::json::push_back(modified_members, member); + } + } + + modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource) + { + resource.data[nmos::fields::nc::members] = modified_members; + + }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { { nmos::nc_property_id(2, 2), nmos::nc_property_change_type::type::value_changed, modified_members } })); + + for (const auto& restore_member : restore_members.as_array()) + { + const auto& filtered_members = boost::copy_range>(reference_members + | boost::adaptors::filtered([&restore_member](const web::json::value& member) + { + return nmos::fields::nc::oid(restore_member) == nmos::fields::nc::oid(member); + }) + ); + if (filtered_members.size() != 1) + { + // can't find this oid in existing members, so member has been added + // Add this resource + // Get example resource from the exising members to get node_id, device_id + const auto& example_monitor = *reference_members.begin(); + const auto& touchpoints = nmos::fields::nc::touchpoints(example_monitor); + + if (touchpoints.size() == 0) + { + // Error + continue; + } + + const auto& touchpoint_uuid = *touchpoints.begin(); + // Get resource + // Get node id and device id + + // Create a source + + // Create a receiver + + + } + } + + return web::json::value::array(); }; } @@ -1923,5 +2080,5 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode .on_channelmapping_activated(make_node_implementation_channelmapping_activation_handler(gate)) .on_control_protocol_property_changed(make_node_implementation_control_protocol_property_changed_handler(gate)) // may be omitted if IS-12 not required .on_filter_property_value_holders(make_filter_property_value_holders_handler(model.control_protocol_resources, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required - .on_modify_rebuildable_block(make_modify_rebuildable_block_handler(model.control_protocol_resources, gate)); // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required + .on_modify_rebuildable_block(make_modify_rebuildable_block_handler(model, gate)); // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required } From 684d3800693b66694a434c50b1afa328efd493f4 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 8 Jan 2025 17:42:30 +0000 Subject: [PATCH 156/250] Fix testApplyBackupDataSet_WithoutCallbacks --- Development/nmos/test/configuration_utils_test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 1dc2a06fb..d1560b840 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -734,6 +734,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) nmos::make_rebuildable(receivers); // root, receivers, mon1 auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + nmos::make_rebuildable(monitor1); nmos::nc_oid monitor_1_oid = oid; nmos::nc_class_id monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 From da9b3c8bd62f9c7642fbd4f3f56753f3bb5655b5 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe <64410119+jonathan-r-thorpe@users.noreply.github.com> Date: Mon, 13 Jan 2025 10:18:24 +0000 Subject: [PATCH 157/250] Apply suggestions from code review Co-authored-by: Simon Lo --- Development/cmake/NmosCppLibraries.cmake | 8 +-- Development/cmake/NmosCppTest.cmake | 2 +- .../nmos-cpp-node/node_implementation.cpp | 11 ++-- Development/nmos/configuration_handlers.h | 4 +- Development/nmos/configuration_methods.cpp | 6 +- Development/nmos/configuration_methods.h | 2 +- Development/nmos/configuration_utils.cpp | 35 ++++++----- Development/nmos/configuration_utils.h | 4 +- .../nmos/control_protocol_resource.cpp | 12 ++-- Development/nmos/control_protocol_resource.h | 4 +- Development/nmos/control_protocol_utils.cpp | 22 +++---- Development/nmos/control_protocol_utils.h | 2 +- Development/nmos/json_fields.h | 2 +- .../nmos/test/configuration_utils_test.cpp | 58 +++++++++---------- 14 files changed, 79 insertions(+), 93 deletions(-) diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index 7cbb33dbb..80a9491bc 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -1007,7 +1007,7 @@ set(NMOS_CPP_NMOS_SOURCES nmos/components.cpp nmos/configuration_api.cpp nmos/configuration_methods.cpp - nmos/configuration_utils.cpp + nmos/configuration_utils.cpp nmos/connection_activation.cpp nmos/connection_api.cpp nmos/connection_events_activation.cpp @@ -1102,9 +1102,9 @@ set(NMOS_CPP_NMOS_HEADERS nmos/components.h nmos/copyable_atomic.h nmos/configuration_api.h - nmos/configuration_handlers.h - nmos/configuration_methods.h - nmos/configuration_utils.h + nmos/configuration_handlers.h + nmos/configuration_methods.h + nmos/configuration_utils.h nmos/connection_activation.h nmos/connection_api.h nmos/connection_events_activation.h diff --git a/Development/cmake/NmosCppTest.cmake b/Development/cmake/NmosCppTest.cmake index ad0eba2a8..9181e1438 100644 --- a/Development/cmake/NmosCppTest.cmake +++ b/Development/cmake/NmosCppTest.cmake @@ -43,7 +43,7 @@ set(NMOS_CPP_TEST_NMOS_TEST_SOURCES nmos/test/api_utils_test.cpp nmos/test/capabilities_test.cpp nmos/test/channels_test.cpp - nmos/test/configuration_utils_test.cpp + nmos/test/configuration_utils_test.cpp nmos/test/control_protocol_test.cpp nmos/test/did_sdid_test.cpp nmos/test/event_type_test.cpp diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 9ef6051fd..97210c708 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1,6 +1,5 @@ #include "node_implementation.h" -#include #include #include #include @@ -1731,7 +1730,7 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control // Example Device Configuration callback for validating a back-up dataset nmos::filter_property_value_holders_handler make_filter_property_value_holders_handler(nmos::resources& resources, slog::base_gate& gate) { - return [&resources, &gate](const nmos::resource& resource, const web::json::value& target_role_path, const web::json::value& property_values, bool recurse, const web::json::value& restore_mode, bool validate, web::json::value& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + return [&resources, &gate](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, const web::json::value& restore_mode, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { // Use this function to filter which of the properties in the object should be modified by the configuration API slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do filter_property_value_holders"; @@ -1740,7 +1739,7 @@ nmos::filter_property_value_holders_handler make_filter_property_value_holders_h auto modifiable_property_value_holders = web::json::value::array(); - for (const auto property_value : property_values.as_array()) + for (const auto& property_value : property_values) { const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); const auto& property_descriptor = nmos::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); @@ -1757,7 +1756,6 @@ nmos::filter_property_value_holders_handler make_filter_property_value_holders_h web::json::push_back(modifiable_property_value_holders, property_value); } } - return modifiable_property_value_holders; }; } @@ -1765,7 +1763,7 @@ nmos::filter_property_value_holders_handler make_filter_property_value_holders_h // Example Device Configuration callback for restoring a back-up dataset nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmos::node_model& model, slog::base_gate& gate) { - return [&model, &gate](const nmos::resource& resource, const web::json::value& target_role_path, const web::json::value& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + return [&model, &gate](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { nmos::resources& resources = model.control_protocol_resources; @@ -1776,7 +1774,7 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo // Error return web::json::value::array(); } - const auto& object_properties_holder = *object_properties_holders.as_array().begin(); + const auto& object_properties_holder = *object_properties_holders.begin(); const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); if (!nmos::is_nc_block(class_id)) @@ -1879,7 +1877,6 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo web::json::push_back(modified_members, member); } } - modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource) { resource.data[nmos::fields::nc::members] = modified_members; diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h index 9c9eca851..8d8c58eb4 100644 --- a/Development/nmos/configuration_handlers.h +++ b/Development/nmos/configuration_handlers.h @@ -19,12 +19,12 @@ namespace nmos } // This callback is invoked if attempting to modify read only properties when restoring a configuration. // This function should modify the Device Model object directly and return a corresponding NcObjectPropertiesSetValidation object - typedef std::function filter_property_value_holders_handler; + typedef std::function filter_property_value_holders_handler; // This callback is invoked if attempting to modify a rebuildable block when restoring a configuration. // This function should handle the modification of the Device Model and any corresponding NMOS resources // and return correpsonding NcObjectPropertiesSetValidation objects for each object modified/added - typedef std::function modify_rebuildable_block_handler; + typedef std::function modify_rebuildable_block_handler; } #endif diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index 6c47a99d0..23e42a437 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -43,9 +43,9 @@ namespace nmos 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 property_value_holders = make_property_value_holders(resource, get_control_protocol_class_descriptor).as_array(); - const auto role_path = get_role_path(resources, resource); + const auto role_path = get_role_path(resources, resource).as_array(); auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, nmos::fields::nc::is_rebuildable(resource.data)); @@ -69,8 +69,6 @@ namespace nmos } } } - - return; } std::size_t generate_validation_fingerprint(const nmos::resources& resources, const nmos::resource& resource) diff --git a/Development/nmos/configuration_methods.h b/Development/nmos/configuration_methods.h index c71b3e123..65d9eaa41 100644 --- a/Development/nmos/configuration_methods.h +++ b/Development/nmos/configuration_methods.h @@ -22,4 +22,4 @@ namespace nmos web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); } -#endif \ No newline at end of file +#endif diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 76efa1336..683626dbc 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -71,17 +71,17 @@ namespace nmos } } - // Check to see if root_role_path is root of role_path_ - bool is_role_path_root(const web::json::value& role_path_root, const web::json::value& role_path_) + // Check to see if root_role_path is root of role_path + bool is_role_path_root(const web::json::array& role_path_root, const web::json::array& role_path) { - if (role_path_root.as_array().size() > role_path_.as_array().size()) + if (role_path_root.size() > role_path.size()) { // root can't be longed that the path return false; } - for (size_t i = 0; i < role_path_root.as_array().size(); ++i) + for (size_t i = 0; i < role_path_root.size(); ++i) { - if (role_path_root.as_array().at(i) != role_path_.as_array().at(i)) + if (role_path_root.at(i) != role_path.at(i)) { return false; } @@ -142,7 +142,7 @@ namespace nmos return false; } - web::json::value modify_device_model(nmos::resources& resources, const nmos::resource& resource, const web::json::value& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value modify_device_model(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) { auto object_properties_set_validation_values = web::json::value::array(); @@ -173,7 +173,7 @@ namespace nmos if (target_object_properties_holders.size() > 1) { // Error in the backup dataset - const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(nmos::fields::nc::path(*target_object_properties_holders.begin()), nmos::nc_restore_validation_status::failed, web::json::value::array(), U("more than one object_properties_holder for role path")); + const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(nmos::fields::nc::path(*target_object_properties_holders.begin()), nmos::nc_restore_validation_status::failed, web::json::value::array().as_array(), U("more than one object_properties_holder for role path")); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); return object_properties_set_validation_values; } @@ -188,12 +188,12 @@ namespace nmos if (modify_rebuildable_block) { // call back to application code which will return an object_properties_set_validation_values object - return modify_rebuildable_block(resource, target_role_path, child_object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor); + return modify_rebuildable_block(resource, target_role_path, child_object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor); } else { // Rebuilding blocks not supported - const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, web::json::value::array(), U("Rebuilding of Device Model blocks not supported")); + const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, web::json::value::array().as_array(), U("Rebuilding of Device Model blocks not supported")); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); return object_properties_set_validation_values; } @@ -212,13 +212,13 @@ namespace nmos // Append the role of the child to the target role path to create the child role path // Hmmmm, there must be a better way of appending the child role to the end of the target role path array... auto child_role_path = web::json::value::array(); - for (const auto& path_element : target_role_path.as_array()) + for (const auto& path_element : target_role_path) { web::json::push_back(child_role_path, path_element); } web::json::push_back(child_role_path, nmos::fields::nc::role(child->data)); - web::json::value child_object_properties_set_validation_values = modify_device_model(resources, *child, child_role_path, child_object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, *child, child_role_path.as_array(), child_object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // Hmmm, there must be a better way of merging two json array objects for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) { @@ -231,7 +231,6 @@ namespace nmos for (const auto& target_object_properties_holder : target_object_properties_holders) { auto property_restore_notices = web::json::value::array(); - // Validate property_values - filter out the incorrect, ignored or unallowed values // Hmm as above, don't like the two step filter process here const auto& filtered_property_values = boost::copy_range>(nmos::fields::nc::values(target_object_properties_holder) @@ -257,12 +256,12 @@ namespace nmos // If the property_modify_list contains read only properties then we call back to the application code to // check that it's OK to change those value. Bear in mind that they could be the class Id, or the oid or some other // property that we don't want changed - property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list, recurse, restore_mode, validate, property_restore_notices, get_control_protocol_class_descriptor); + property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list.as_array(), recurse, restore_mode, validate, property_restore_notices.as_array(), get_control_protocol_class_descriptor); } else { // Modify of read only properties not supported - const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, property_restore_notices, U("Modification of read only properties not supported")); + const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, property_restore_notices.as_array(), U("Modification of read only properties not supported")); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); continue; } @@ -285,7 +284,7 @@ namespace nmos }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { {property_id, nmos::nc_property_change_type::type::value_changed, value} })); } } - const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, property_restore_notices, U("OK")); + const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, property_restore_notices.as_array(), U("OK")); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); } @@ -326,7 +325,7 @@ namespace nmos { auto object_properties_set_validation_values = web::json::value::array(); - const auto target_role_path = get_role_path(resources, resource); + const auto target_role_path = get_role_path(resources, resource).as_array(); // Detect and warn if there are any object_properties_holders outside of the target role path's scope // Hmmm, can this be done as a one step process rather than filtering and then iterating over filtered list? @@ -338,7 +337,7 @@ namespace nmos ); for (const auto& orphan_object_properties_holder : orphan_object_properties_holders) { - const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(nmos::fields::nc::path(orphan_object_properties_holder), nmos::nc_restore_validation_status::not_found, web::json::value::array(), U("object role path not found under target role path")); + const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(nmos::fields::nc::path(orphan_object_properties_holder), nmos::nc_restore_validation_status::not_found, web::json::value::array().as_array(), U("object role path not found under target role path")); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); } @@ -351,4 +350,4 @@ namespace nmos return object_properties_set_validation_values; } -} \ No newline at end of file +} diff --git a/Development/nmos/configuration_utils.h b/Development/nmos/configuration_utils.h index cd6132538..a026b1e37 100644 --- a/Development/nmos/configuration_utils.h +++ b/Development/nmos/configuration_utils.h @@ -10,7 +10,7 @@ namespace nmos struct control_protocol_resource; // Check to see if role_path is sub path of parent_role_path - bool is_role_path_root(const web::json::value& role_path_, const web::json::value& parent_role_path); + bool is_role_path_root(const web::json::array& role_path_, const web::json::array& parent_role_path); bool is_block_modified(const nmos::resource& resource, const web::json::value& object_properties_holder); @@ -20,4 +20,4 @@ namespace nmos web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); } -#endif \ No newline at end of file +#endif diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 393bbfb6d..22b4c0ac3 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -871,13 +871,13 @@ namespace nmos } // TODO: add link - web::json::value make_nc_object_properties_holder(const web::json::value& role_path, const web::json::value& property_value_holders, bool is_rebuildable) + web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_value_holders, bool is_rebuildable) { using web::json::value_of; return value_of({ - { nmos::fields::nc::path, role_path }, - { nmos::fields::nc::values, property_value_holders}, + { nmos::fields::nc::path, web::json::value_from_elements(role_path)}, + { nmos::fields::nc::values, web::json::value_from_elements(property_value_holders)}, { nmos::fields::nc::is_rebuildable, is_rebuildable} }, true ); @@ -900,15 +900,15 @@ namespace nmos } // TODO: add link - web::json::value make_nc_object_properties_set_validation(const web::json::value& role_path, nc_restore_validation_status::status status, const web::json::value& notices, const utility::string_t& status_message) + web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const utility::string_t& status_message) { using web::json::value; using web::json::value_of; return value_of({ - { nmos::fields::nc::path, role_path}, + { nmos::fields::nc::path, web::json::value_from_elements(role_path)}, { nmos::fields::nc::status, value::number(status)}, - { nmos::fields::nc::notices, notices }, + { nmos::fields::nc::notices, web::json::value_from_elements(notices)}, { nmos::fields::nc::status_message, value::string(status_message)} }, true ); diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 72365d583..e2b74f45d 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -202,13 +202,13 @@ namespace nmos web::json::value make_nc_property_value_holder(const nc_property_id& property_id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, const web::json::value& property_value); // TODO: add link - web::json::value make_nc_object_properties_holder(const web::json::value& role_path, const web::json::value& property_value_holders, bool is_rebuildable); + web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_value_holders, bool is_rebuildable); // TODO: add link web::json::value make_nc_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message); // TODO: add link - web::json::value make_nc_object_properties_set_validation(const web::json::value& role_path, nc_restore_validation_status::status status, const web::json::value& notices, const utility::string_t& status_message); + web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const utility::string_t& status_message); } // command message response diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index e18e89ff7..8bb52928e 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -343,7 +343,6 @@ namespace nmos { const auto& members = nmos::fields::nc::members(parent_nc_block_resource.data); - const auto role_path_segement = web::json::front(role_path_segments); role_path_segments.erase(0); // find the role_path_segment member @@ -376,7 +375,6 @@ namespace nmos return web::json::value{}; } - typedef std::function get_property_descriptors_handler; // generic find control class property descriptor in property_descriptor_ array (NcPropertyDescriptor) web::json::value find_property_descriptor(const nc_property_id& property_id, const nc_class_id& class_id_, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) @@ -388,12 +386,12 @@ namespace nmos while (!class_id.empty()) { const auto& control_class = get_control_protocol_class_descriptor(class_id); - const auto& property_descriptors = control_class.property_descriptors; - auto found = std::find_if(property_descriptors.as_array().begin(), property_descriptors.as_array().end(), [&property_id](const web::json::value& property_descriptor) + const auto& property_descriptors = control_class.property_descriptors.as_array(); + auto found = std::find_if(property_descriptors.begin(), property_descriptors.end(), [&property_id](const web::json::value& property_descriptor) { return (property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor))); }); - if (property_descriptors.as_array().end() != found) { return *found; } + if (property_descriptors.end() != found) { return *found; } class_id.pop_back(); } @@ -687,9 +685,9 @@ namespace nmos } } - resources::const_iterator find_control_protocol_resource_by_role_path(const resources& resources, const web::json::value& role_path_) + resources::const_iterator find_control_protocol_resource_by_role_path(const resources& resources, const web::json::array& role_path_) { - web::json::value role_path = role_path_; + auto role_path = role_path_; auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); if (resources.end() != resource) { @@ -727,19 +725,13 @@ namespace nmos std::list role_path_segments; boost::algorithm::split(role_path_segments, role_path_, [](utility::char_t c) { return '.' == c; }); - web::json::value role_path = web::json::value::array(); - - for (auto item : role_path_segments) - { - web::json::push_back(role_path, utility::string_t(item.c_str())); - } - return role_path; + return web::json::value_from_elements(role_path_segments); } resources::const_iterator find_control_protocol_resource_by_role_path(const resources& resources, const utility::string_t& role_path_) { - const auto& role_path = parse_role_path(role_path_); + const auto& role_path = parse_role_path(role_path_).as_array(); return find_control_protocol_resource_by_role_path(resources, role_path); } diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index 7b7e85368..d15605178 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -78,7 +78,7 @@ namespace nmos resources::const_iterator find_control_protocol_resource(resources& resources, type type, const id& id); // find resource based on role path. - resources::const_iterator find_control_protocol_resource_by_role_path(const resources& resources, const web::json::value& role_path); + resources::const_iterator find_control_protocol_resource_by_role_path(const resources& resources, const web::json::array& role_path); // find resource based on role path. Roles in role path string must be delimited with a '.' resources::const_iterator find_control_protocol_resource_by_role_path(const resources& resources, const utility::string_t& role_path); diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index 58aa5915c..47caed188 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -312,7 +312,7 @@ namespace nmos const web::json::field_as_array fields{ U("fields") }; // sequence const web::json::field_as_integer generic_state{ U("generic") }; // NcDeviceGenericState const web::json::field_as_string device_specific_details{ U("deviceSpecificDetails") }; - const web::json::field_as_value path{ U("path") }; // NcRolePath + const web::json::field_as_array path{ U("path") }; // NcRolePath const web::json::field_as_bool case_sensitive{ U("caseSensitive") }; const web::json::field_as_bool match_whole_string{ U("matchWholeString") }; const web::json::field_as_bool include_derived{ U("includeDerived") }; diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index d1560b840..bed19f411 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -13,32 +13,32 @@ BST_TEST_CASE(testIsRolePathRoot) { { - web::json::value role_path = web::json::value_of({ U("root"), U("path1")}); - web::json::value role_path_root = web::json::value_of({ U("root"), U("path1")}); + auto role_path = web::json::value_of({ U("root"), U("path1") }).as_array(); + auto role_path_root = web::json::value_of({ U("root"), U("path1") }).as_array(); BST_REQUIRE(nmos::is_role_path_root(role_path_root, role_path)); } { - web::json::value role_path = web::json::value_of({ U("root"), U("path1"), U("path2"), U("path3")}); - web::json::value role_path_root = web::json::value_of({ U("root"), U("path1"), U("path2") }); + auto role_path = web::json::value_of({ U("root"), U("path1"), U("path2"), U("path3") }).as_array(); + auto role_path_root = web::json::value_of({ U("root"), U("path1"), U("path2") }).as_array(); BST_REQUIRE(nmos::is_role_path_root(role_path_root, role_path)); } { - web::json::value role_path = web::json::value_of({ U("root"), U("path1")}); - web::json::value role_path_root = web::json::value_of({ U("root"), U("path1"), U("path2") }); + auto role_path = web::json::value_of({ U("root"), U("path1") }).as_array(); + auto role_path_root = web::json::value_of({ U("root"), U("path1"), U("path2") }).as_array(); BST_REQUIRE(!nmos::is_role_path_root(role_path_root, role_path)); } { - web::json::value role_path = web::json::value_of({ U("root"), U("path3"), U("path4") }); - web::json::value role_path_root = web::json::value_of({ U("root"), U("path1"), U("path2") }); + auto role_path = web::json::value_of({ U("root"), U("path3"), U("path4") }).as_array(); + auto role_path_root = web::json::value_of({ U("root"), U("path1"), U("path2") }).as_array(); BST_REQUIRE(!nmos::is_role_path_root(role_path_root, role_path)); } { - web::json::value role_path = web::json::value_of({ U("path3"), U("path4") }); - web::json::value role_path_root = web::json::value_of({ U("root"), U("path1"), U("path2") }); + auto role_path = web::json::value_of({ U("path3"), U("path4") }).as_array(); + auto role_path_root = web::json::value_of({ U("root"), U("path1"), U("path2") }).as_array(); BST_REQUIRE(!nmos::is_role_path_root(role_path_root, role_path)); } @@ -91,7 +91,7 @@ BST_TEST_CASE(testIsBlockModified) nmos::nc_property_id property_id(2, 2); // block members web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); BST_CHECK(!nmos::is_block_modified(receivers, object_properties_holder)); } @@ -107,7 +107,7 @@ BST_TEST_CASE(testIsBlockModified) nmos::nc_property_id property_id(2, 2); // block members web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -129,7 +129,7 @@ BST_TEST_CASE(testIsBlockModified) nmos::nc_property_id property_id(2, 2); // block members web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -151,7 +151,7 @@ BST_TEST_CASE(testIsBlockModified) nmos::nc_property_id property_id(2, 2); // block members web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -173,7 +173,7 @@ BST_TEST_CASE(testIsBlockModified) nmos::nc_property_id property_id(2, 2); // block members web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -195,7 +195,7 @@ BST_TEST_CASE(testIsBlockModified) nmos::nc_property_id property_id(2, 2); // block members web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -217,7 +217,7 @@ BST_TEST_CASE(testIsBlockModified) nmos::nc_property_id property_id(2, 2); // block members web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -269,7 +269,7 @@ BST_TEST_CASE(testGetRolePath) for (const auto& expected_role_path : expected_role_paths.as_array()) { - const auto& resource = find_control_protocol_resource_by_role_path(resources, expected_role_path); + const auto& resource = find_control_protocol_resource_by_role_path(resources, expected_role_path.as_array()); value actual_role_path = nmos::get_role_path(resources, *resource); BST_CHECK_EQUAL(expected_role_path, actual_role_path); } @@ -321,22 +321,22 @@ BST_TEST_CASE(testApplyBackupDataSet) bool modify_rebuildable_block_called = false; // callback stubs - nmos::filter_property_value_holders_handler filter_property_value_holders = [&](const nmos::resource& resource, const web::json::value& target_role_path, const web::json::value& property_values, bool recurse, const web::json::value& restore_mode, bool validate, web::json::value& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::filter_property_value_holders_handler filter_property_value_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, const web::json::value& restore_mode, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { filter_property_value_holders_called = true; auto modifiable_property_value_holders = web::json::value::array(); - for (const auto property_value : property_values.as_array()) + for (const auto& property_value : property_values) { web::json::push_back(modifiable_property_value_holders, property_value); } return modifiable_property_value_holders; }; - nmos::modify_rebuildable_block_handler modify_rebuildable_block = [&](const nmos::resource& resource, const web::json::value& target_role_path, const web::json::value& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::modify_rebuildable_block_handler modify_rebuildable_block = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { modify_rebuildable_block_called = true; value out = value::array(); - const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, value::array(), U("OK")); + const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, value::array().as_array(), U("OK")); web::json::push_back(out, object_properties_set_validation); return out; }; @@ -350,21 +350,21 @@ BST_TEST_CASE(testApplyBackupDataSet) value property_value_holders = value::array(); value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); push_back(object_properties_holders, object_properties_holder); value target_role_path = value_of({ U("root"), U("receivers")}); bool recurse = true; bool validate = true; web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::modify; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); value object_properties_set_validation = output.as_array().at(0); - BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); @@ -386,7 +386,7 @@ BST_TEST_CASE(testApplyBackupDataSet) // This is a read only property value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths value target_role_path = value_of({ U("root"), U("receivers") }); @@ -394,7 +394,7 @@ BST_TEST_CASE(testApplyBackupDataSet) web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; bool validate = true; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one @@ -424,7 +424,7 @@ BST_TEST_CASE(testApplyBackupDataSet) // This is a read only property value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths value target_role_path = value_of({ U("root"), U("receivers") }); @@ -432,7 +432,7 @@ BST_TEST_CASE(testApplyBackupDataSet) web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; bool validate = true; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one From 13490755f5fc65b207118ec93698f8fa2f757e76 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 13 Jan 2025 10:23:30 +0000 Subject: [PATCH 158/250] Implement rebuildable block handler --- .../nmos-cpp-node/node_implementation.cpp | 232 ++++++++++++++---- 1 file changed, 181 insertions(+), 51 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 97210c708..cf10748ea 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -21,6 +21,7 @@ #include "nmos/colorspace.h" #include "nmos/configuration_handlers.h" #include "nmos/configuration_methods.h" +#include "nmos/configuration_utils.h" #include "nmos/connection_resources.h" #include "nmos/connection_events_activation.h" #include "nmos/control_protocol_resources.h" @@ -1769,12 +1770,24 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do modify_rebuildable_block"; - if (object_properties_holders.size() != 1) + // Validate the object_properties_holder + + // Find object_properties_holder for resource + const auto& filtered_holders = boost::copy_range>(object_properties_holders.as_array() + | boost::adaptors::filtered([&resources, &resource](const web::json::value& object_properties_holder) + { + return nmos::fields::nc::path(object_properties_holder) == nmos::get_role_path(resources, resource); + }) + ); + + if (filtered_holders.size() != 1) { + // Either can't find associated object_properties_holder, or there's more than one (ambiguous) // Error return web::json::value::array(); } - const auto& object_properties_holder = *object_properties_holders.begin(); + + const auto& object_properties_holder = *filtered_holders.begin(); const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); if (!nmos::is_nc_block(class_id)) @@ -1799,8 +1812,9 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo const auto& restore_members = nmos::fields::nc::value(members_property_holder); const auto& reference_members = nmos::fields::nc::members(resource.data); - std::vector members_to_remove; + std::vector members_to_remove; + // Iterate through the members of the block and compare to the members in the backup dataset for (const auto& reference_member : reference_members) { const auto& filtered_members = boost::copy_range>(restore_members.as_array() @@ -1812,77 +1826,55 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo if (filtered_members.size() != 1) { // can't find this oid in restore dataset, so member has been removed - // Remove this resource + // get the receiver monitor resource auto found = nmos::find_resource_if(resources, nmos::types::nc_receiver_monitor, [&reference_member](const nmos::resource& resource) { return nmos::fields::nc::oid(reference_member) == nmos::fields::nc::oid(resource.data); }); - -// const auto& receiver_monitor = *(reference_members.begin()); + + // use the touchpoint UUID to idenity the receiver resource linked to the receiver resource const auto& touchpoints = found->data.at(nmos::fields::nc::touchpoints); - if (touchpoints.size() == 0) { // Error continue; } + // erase receiver NMOS resource and receiver monitor Device Model resource const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); - const auto& receiver = nmos::find_resource(resources, touchpoint_uuid.as_string()); { - const auto& lock = model.write_lock(); - // remove receiver - bool success = erase_resource(model.node_resources, nmos::fields::id(receiver->data)); + const auto lock = model.write_lock(); + bool success = erase_resource(model.node_resources, touchpoint_uuid.as_string()); if (!success) { // Error continue; } - const auto oid = found->id; - utility::ostringstream_t id_str; - id_str << oid; - success = erase_resource(resources, oid); + const auto oid = nmos::fields::nc::oid(found->data); + success = erase_resource(resources, found->id); if (success) { members_to_remove.push_back(oid); } } } - const auto restore_member = *filtered_members.begin(); - // We ignore the description and user label as these are non-normative - if (nmos::fields::nc::role(reference_member) != nmos::fields::nc::role(restore_member) - || nmos::fields::nc::constant_oid(reference_member) != nmos::fields::nc::constant_oid(restore_member) - || nmos::fields::nc::class_id(reference_member) != nmos::fields::nc::class_id(restore_member) - || nmos::fields::nc::owner(reference_member) != nmos::fields::nc::owner(restore_member)) + else { - // Modify existing resource - // in this example we will ignore/reject changes to the role, constant_oid, class_id or owner - // Do nothing, return warning - continue; - } - } - auto modified_members = web::json::value::array(); - - for (const auto& member : reference_members) - { - const auto& remove_member = boost::copy_range>(members_to_remove | boost::adaptors::filtered([&member](const nmos::id& oid) + const auto restore_member = *filtered_members.begin(); + // We ignore the description and user label as these are non-normative + if (nmos::fields::nc::role(reference_member) != nmos::fields::nc::role(restore_member) + || nmos::fields::nc::constant_oid(reference_member) != nmos::fields::nc::constant_oid(restore_member) + || nmos::fields::nc::class_id(reference_member) != nmos::fields::nc::class_id(restore_member) + || nmos::fields::nc::owner(reference_member) != nmos::fields::nc::owner(restore_member)) { - return oid == nmos::fields::id(member); - }) - ); - - if (remove_member.size() == 0) - { - web::json::push_back(modified_members, member); + // Modify existing resource + // in this example we will ignore/reject changes to the role, constant_oid, class_id or owner + // Do nothing, return warning + continue; + } } } - modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource) - { - resource.data[nmos::fields::nc::members] = modified_members; - - }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { { nmos::nc_property_id(2, 2), nmos::nc_property_change_type::type::value_changed, modified_members } })); - for (const auto& restore_member : restore_members.as_array()) { const auto& filtered_members = boost::copy_range>(reference_members @@ -1896,8 +1888,18 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo // can't find this oid in existing members, so member has been added // Add this resource // Get example resource from the exising members to get node_id, device_id + if (reference_members.size() == 0) + { + // Error + continue; + } const auto& example_monitor = *reference_members.begin(); - const auto& touchpoints = nmos::fields::nc::touchpoints(example_monitor); + auto found = nmos::find_resource_if(resources, nmos::types::nc_receiver_monitor, [&example_monitor](const nmos::resource& resource) + { + return nmos::fields::nc::oid(example_monitor) == nmos::fields::nc::oid(resource.data); + }); + + const auto& touchpoints = found->data.at(nmos::fields::nc::touchpoints); if (touchpoints.size() == 0) { @@ -1905,18 +1907,146 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo continue; } - const auto& touchpoint_uuid = *touchpoints.begin(); - // Get resource - // Get node id and device id + const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); + const auto& found2 = nmos::find_resource(resources, touchpoint_uuid.as_string()); + + const auto& device_id = nmos::fields::device_id(found2->data); + const auto& interface_bindings = nmos::fields::interface_bindings(found2->data); + const auto& video_type = nmos::fields::format(found2->data); + + // calculate child resource role path + const auto oid = nmos::fields::nc::oid(restore_member); - // Create a source + const auto& target_role_path = nmos::get_role_path(resources, resource); - // Create a receiver + // Hmmmm, there must be a better way of appending the child role to the end of the target role path array... + auto child_role_path = web::json::value::array(); + for (const auto& path_element : target_role_path.as_array()) + { + web::json::push_back(child_role_path, path_element); + } + web::json::push_back(child_role_path, nmos::fields::nc::role(restore_member)); + + // Find the object_properties_holder that describes the new receiver monitor + const auto& filtered_child_object_properties_holders = boost::copy_range>(object_properties_holders.as_array() + | boost::adaptors::filtered([&child_role_path](const web::json::value& object_properties_holder) + { + return nmos::fields::nc::path(object_properties_holder) == child_role_path; + }) + ); + + if (filtered_child_object_properties_holders.size() != 1) + { + // Error + continue; + } + + const auto& child_object_properties_holder = *filtered_child_object_properties_holders.begin(); + + const auto& oid_properties_holders = boost::copy_range>(nmos::fields::nc::values(child_object_properties_holder) + | boost::adaptors::filtered([](const web::json::value& property_value_holder) + { + return nmos::nc_property_id(1, 2) == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value_holder)); + }) + ); + // There should only be a single property holder for the members + if (oid_properties_holders.size() != 1) + { + // Error + return web::json::value::array(); + } + + const auto& oid_property_holder = *block_members_properties_holders.begin(); + const auto& oid = nmos::fields::nc::value(oid_property_holder); + + auto found3 = nmos::find_resource_if(resources, nmos::types::nc_receiver_monitor, [&oid](const nmos::resource& resource) + { + return oid == nmos::fields::nc::oid(resource.data); + }); + + const auto& touchpoints2 = found3->data.at(nmos::fields::nc::touchpoints); + + if (touchpoints2.size() == 0) + { + // Error + continue; + } + + const auto& touchpoint_uuid2 = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints2.as_array().begin())); + + // Make resources + // + const auto host_interfaces = nmos::get_host_interfaces(model.settings); + const auto& host_address = nmos::fields::host_address(model.settings); + // the interface corresponding to the host address is used for the example node's WebSocket senders and receivers + const auto host_interface_ = impl::find_interface(host_interfaces, host_address); + if (host_interfaces.end() == host_interface_) + { + slog::log(gate, SLOG_FLF) << "No network interface corresponding to host_address?"; + throw node_implementation_init_exception(); + } + const auto& host_interface = *host_interface_; + + const auto& primary_address = model.settings.has_field(nmos::fields::host_addresses) ? web::json::front(nmos::fields::host_addresses(model.settings)).as_string() : host_address; + const auto& secondary_address = model.settings.has_field(nmos::fields::host_addresses) ? web::json::back(nmos::fields::host_addresses(model.settings)).as_string() : host_address; + const auto primary_interface_ = impl::find_interface(host_interfaces, primary_address); + const auto secondary_interface_ = impl::find_interface(host_interfaces, secondary_address); + if (host_interfaces.end() == primary_interface_ || host_interfaces.end() == secondary_interface_) + { + slog::log(gate, SLOG_FLF) << "No network interface corresponding to one of the host_addresses?"; + throw node_implementation_init_exception(); + } + const auto& primary_interface = *primary_interface_; + const auto& secondary_interface = *secondary_interface_; + const auto smpte2022_7 = impl::fields::smpte2022_7(model.settings); + const auto interface_names = smpte2022_7 + ? std::vector{ primary_interface.name, secondary_interface.name } + : std::vector{ primary_interface.name }; + + const auto& receiver = nmos::make_receiver(touchpoint_uuid2.as_string(), device_id, nmos::transports::rtp, interface_names, model.settings); + + // Create receiver monitor + + + // Hmmmmmmmmmmmmmmmmmmmmmmmmmmmmm + // create some helper functions to do things like: + // - find an nmos resource assosiated with a control protocol resource (via touchpoint) + // - find an nmos resource assosiated with a control protocol resource (via touchpoint) + // - manipulate the object_properties_holder to create a json resource based on the object_properties_holders so don't have to keep querying json + // - functions to compare device model to object_properties_holder to show differences + + //const auto receiver_monitor = nmos::make_receiver_monitor(oid, true, receiver_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, receiver_id}) } })); } } + // Update the members of the receivers block + if (members_to_remove.size() > 0) + { + auto modified_members = web::json::value::array(); + + for (const auto& member : reference_members) + { + const auto& remove_member = boost::copy_range>(members_to_remove | boost::adaptors::filtered([&member](int oid) + { + return oid == nmos::fields::nc::oid(member); + }) + ); + + if (remove_member.size() == 0) + { + web::json::push_back(modified_members, member); + } + } + + modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource) + { + resource.data[nmos::fields::nc::members] = modified_members; + + }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { { nmos::nc_property_id(2, 2), nmos::nc_property_change_type::type::value_changed, modified_members } })); + } + return web::json::value::array(); }; } From 80a73aec27ecbfc993d024c148d9ce3d89b22393 Mon Sep 17 00:00:00 2001 From: "Simon Lo (Sony)" Date: Mon, 13 Jan 2025 13:55:26 +0000 Subject: [PATCH 159/250] Changes from review --- .../nmos/test/configuration_utils_test.cpp | 444 +++++++++--------- 1 file changed, 222 insertions(+), 222 deletions(-) diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index bed19f411..8f8b6f87b 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -19,7 +19,7 @@ BST_TEST_CASE(testIsRolePathRoot) BST_REQUIRE(nmos::is_role_path_root(role_path_root, role_path)); } { - auto role_path = web::json::value_of({ U("root"), U("path1"), U("path2"), U("path3") }).as_array(); + auto role_path = web::json::value_of({ U("root"), U("path1"), U("path2"), U("path3")}).as_array(); auto role_path_root = web::json::value_of({ U("root"), U("path1"), U("path2") }).as_array(); BST_REQUIRE(nmos::is_role_path_root(role_path_root, role_path)); @@ -53,16 +53,16 @@ BST_TEST_CASE(testIsBlockModified) // Create Device Model // root auto root_block = nmos::make_root_block(); - nmos::nc_oid oid = nmos::root_block_oid; + auto oid = nmos::root_block_oid; // root, receivers auto receivers = nmos::make_block(++oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); - nmos::nc_oid receiver_block_oid = oid; + auto receiver_block_oid = oid; // root, receivers, mon1 auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); - nmos::nc_oid monitor_1_oid = oid; + auto monitor_1_oid = oid; // root, receivers, mon2 auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); - nmos::nc_oid monitor_2_oid = oid; + auto monitor_2_oid = oid; nmos::push_back(receivers, monitor1); // add example-control to root-block nmos::push_back(receivers, monitor2); @@ -70,154 +70,154 @@ BST_TEST_CASE(testIsBlockModified) nmos::push_back(root_block, receivers); // Create Object Properties Holder - value role_path = value::array(); + auto role_path = value::array(); push_back(role_path, U("root")); push_back(role_path, U("receivers")); // Members unchanged { - value property_value_holders = value::array(); + auto property_value_holders = value::array(); - value members = value::array(); - nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + auto members = value::array(); + const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); { - value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); push_back(members, block_member_descriptor); } { - value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - nmos::nc_property_id property_id(2, 2); // block members - web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + const nmos::nc_property_id property_id(2, 2); // block members + const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); BST_CHECK(!nmos::is_block_modified(receivers, object_properties_holder)); } // Changed number of members { - value property_value_holders = value::array(); + auto property_value_holders = value::array(); - value members = value::array(); - nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); - value block_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, class_id, U("label"), receiver_block_oid); + auto members = value::array(); + const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + const auto block_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, class_id, U("label"), receiver_block_oid); push_back(members, block_descriptor); - nmos::nc_property_id property_id(2, 2); // block members - web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + const nmos::nc_property_id property_id(2, 2); // block members + const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } // Changed oids { - value property_value_holders = value::array(); + auto property_value_holders = value::array(); - value members = value::array(); - nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + auto members = value::array(); + const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); { - value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), 10, true, class_id, U("monitor 1"), receiver_block_oid); + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), 10, true, class_id, U("monitor 1"), receiver_block_oid); push_back(members, block_member_descriptor); } { - value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), 20, true, class_id, U("monitor 2"), receiver_block_oid); + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), 20, true, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - nmos::nc_property_id property_id(2, 2); // block members - web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + const nmos::nc_property_id property_id(2, 2); // block members + const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } // Changed roles { - value property_value_holders = value::array(); + auto property_value_holders = value::array(); - value members = value::array(); - nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + auto members = value::array(); + const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); { - value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon3"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon3"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); push_back(members, block_member_descriptor); } { - value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon4"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon4"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - nmos::nc_property_id property_id(2, 2); // block members - web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + const nmos::nc_property_id property_id(2, 2); // block members + const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } // Changed class id { - value property_value_holders = value::array(); + auto property_value_holders = value::array(); - value members = value::array(); - nmos::nc_class_id class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 1 }); + auto members = value::array(); + const auto class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 1 }); { - value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); push_back(members, block_member_descriptor); } { - value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - nmos::nc_property_id property_id(2, 2); // block members - web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + const nmos::nc_property_id property_id(2, 2); // block members + const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } // Changed owner oid { - value property_value_holders = value::array(); + auto property_value_holders = value::array(); - value members = value::array(); - nmos::nc_class_id class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 1 }); + auto members = value::array(); + const auto class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 1 }); { - value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), 20); + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), 20); push_back(members, block_member_descriptor); } { - value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), 20); + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), 20); push_back(members, block_member_descriptor); } - nmos::nc_property_id property_id(2, 2); // block members - web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + const nmos::nc_property_id property_id(2, 2); // block members + const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } // Changed constant oid { - value property_value_holders = value::array(); + auto property_value_holders = value::array(); - value members = value::array(); - nmos::nc_class_id class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 1 }); + auto members = value::array(); + const auto class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 1 }); { - value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, false, class_id, U("monitor 1"), receiver_block_oid); + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, false, class_id, U("monitor 1"), receiver_block_oid); push_back(members, block_member_descriptor); } { - value block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, false, class_id, U("monitor 2"), receiver_block_oid); + const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, false, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - nmos::nc_property_id property_id(2, 2); // block members - web::json::value property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + const nmos::nc_property_id property_id(2, 2); // block members + const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -235,7 +235,7 @@ BST_TEST_CASE(testGetRolePath) // root auto root_block = nmos::make_root_block(); - nmos::nc_oid oid = nmos::root_block_oid; + auto oid = nmos::root_block_oid; // root, ClassManager auto class_manager = nmos::make_class_manager(++oid, control_protocol_state); nmos::nc_oid receiver_block_oid = ++oid; @@ -260,7 +260,7 @@ BST_TEST_CASE(testGetRolePath) insert_resource(resources, std::move(monitor1)); insert_resource(resources, std::move(monitor2)); - value expected_role_paths = value::array(); + auto expected_role_paths = value::array(); push_back(expected_role_paths, value_of({ U("root") })); push_back(expected_role_paths, value_of({ U("root"), U("ClassManager")})); push_back(expected_role_paths, value_of({ U("root"), U("receivers") })); @@ -270,8 +270,8 @@ BST_TEST_CASE(testGetRolePath) for (const auto& expected_role_path : expected_role_paths.as_array()) { const auto& resource = find_control_protocol_resource_by_role_path(resources, expected_role_path.as_array()); - value actual_role_path = nmos::get_role_path(resources, *resource); - BST_CHECK_EQUAL(expected_role_path, actual_role_path); + const auto actual_role_path = nmos::get_role_path(resources, *resource); + BST_CHECK_EQUAL(expected_role_path.as_array(), actual_role_path); } } @@ -288,10 +288,10 @@ BST_TEST_CASE(testApplyBackupDataSet) // Create Device Model // root auto root_block = nmos::make_root_block(); - nmos::nc_oid oid = nmos::root_block_oid; + auto oid = nmos::root_block_oid; // root, ClassManager auto class_manager = nmos::make_class_manager(++oid, control_protocol_state); - nmos::nc_oid receiver_block_oid = ++oid; + auto receiver_block_oid = ++oid; // root, receivers auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); nmos::make_rebuildable(receivers); @@ -299,9 +299,9 @@ BST_TEST_CASE(testApplyBackupDataSet) auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({{nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); // make monitor1 rebuildable nmos::make_rebuildable(monitor1); - - nmos::nc_oid monitor_1_oid = oid; - nmos::nc_class_id monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + + auto monitor_1_oid = oid; + auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); nmos::push_back(receivers, monitor1); @@ -319,12 +319,12 @@ BST_TEST_CASE(testApplyBackupDataSet) bool filter_property_value_holders_called = false; bool modify_rebuildable_block_called = false; - + // callback stubs nmos::filter_property_value_holders_handler filter_property_value_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, const web::json::value& restore_mode, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { filter_property_value_holders_called = true; - auto modifiable_property_value_holders = web::json::value::array(); + auto modifiable_property_value_holders = value::array(); for (const auto& property_value : property_values) { @@ -335,7 +335,7 @@ BST_TEST_CASE(testApplyBackupDataSet) nmos::modify_rebuildable_block_handler modify_rebuildable_block = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { modify_rebuildable_block_called = true; - value out = value::array(); + auto out = value::array(); const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, value::array().as_array(), U("OK")); web::json::push_back(out, object_properties_set_validation); return out; @@ -345,24 +345,24 @@ BST_TEST_CASE(testApplyBackupDataSet) // Check the successful modification of the "enabled" flag of mon1's worker base class in Modify mode // // Create Object Properties Holder - value object_properties_holders = value::array(); - value role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - value property_value_holders = value::array(); - value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); push_back(object_properties_holders, object_properties_holder); - value target_role_path = value_of({ U("root"), U("receivers")}); + const auto target_role_path = value_of({ U("root"), U("receivers")}); bool recurse = true; bool validate = true; - web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::modify; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); - value object_properties_set_validation = output.as_array().at(0); + auto object_properties_set_validation = output.as_array().at(0); BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); @@ -374,71 +374,71 @@ BST_TEST_CASE(testApplyBackupDataSet) } { // Check filter_property_value_holders_handler is called when changing a read only property of rebuildable object in Rebuild mode - // + // filter_property_value_holders_called = false; modify_rebuildable_block_called = false; // Create Object Properties Holder - value object_properties_holders = value::array(); - value role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - value property_value_holders = value::array(); - nmos::nc_property_id property_id(2, 1); + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const nmos::nc_property_id property_id(2, 1); // This is a read only property - value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths - value target_role_path = value_of({ U("root"), U("receivers") }); + const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; - web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; bool validate = true; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); - value object_properties_set_validation = output.as_array().at(0); + const auto object_properties_set_validation = output.as_array().at(0); // make sure the validation status propagates from the callback BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); - // expecting callback to filter_property_value_holders_called + // expecting callback to filter_property_value_holders_called // but not to modify_rebuildable_block_called BST_CHECK(filter_property_value_holders_called); BST_CHECK(!modify_rebuildable_block_called); } { // Check error generated when attempting to change a read only property of non-rebuidable object in Rebuild mode - // + // filter_property_value_holders_called = false; modify_rebuildable_block_called = false; // Create Object Properties Holder - value object_properties_holders = value::array(); - value role_path = value_of({ U("root"), U("receivers"), U("mon2") }); - value property_value_holders = value::array(); - nmos::nc_property_id property_id(2, 1); + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + auto property_value_holders = value::array(); + const nmos::nc_property_id property_id(2, 1); // This is a read only property - value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths - value target_role_path = value_of({ U("root"), U("receivers") }); + const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; - web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; bool validate = true; const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); - value object_properties_set_validation = output.as_array().at(0); + const auto object_properties_set_validation = output.as_array().at(0); // make sure the validation status propagates from the callback BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); @@ -453,7 +453,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); - // expecting callback to filter_property_value_holders_called + // expecting callback to filter_property_value_holders_called // but not to modify_rebuildable_block_called BST_CHECK(!filter_property_value_holders_called); BST_CHECK(!modify_rebuildable_block_called); @@ -466,29 +466,29 @@ BST_TEST_CASE(testApplyBackupDataSet) // Change a read only property in Rebuild mode // Create Object Properties Holder - value object_properties_holders = value::array(); - value role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - value property_value_holders = value::array(); - nmos::nc_property_id property_id(2, 1); + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const nmos::nc_property_id property_id(2, 1); // This is a read only property push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), true, value("change this value"))); // This is a writable property push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false)); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths - value target_role_path = value_of({ U("root"), U("receivers") }); + const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; - web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::modify; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; bool validate = true; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); - value object_properties_set_validation = output.as_array().at(0); + const auto object_properties_set_validation = output.as_array().at(0); // expect overall status for object to be OK as although the read only property change should fail // the writable property should succeed @@ -514,27 +514,27 @@ BST_TEST_CASE(testApplyBackupDataSet) modify_rebuildable_block_called = false; // Create Object Properties Holder - value object_properties_holders = value::array(); - value role_path = value_of({ U("root"), U("receivers")}); - value property_value_holders = value::array(); - value members = value::array(); - + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers")}); + auto property_value_holders = value::array(); + auto members = value::array(); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false)); - value target_role_path = value_of({ U("root"), U("receivers") }); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); + const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; - web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); - value object_properties_set_validation = output.as_array().at(0); + const auto object_properties_set_validation = output.as_array().at(0); - BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); @@ -548,27 +548,27 @@ BST_TEST_CASE(testApplyBackupDataSet) modify_rebuildable_block_called = false; // Create Object Properties Holder - value object_properties_holders = value::array(); - value role_path = value_of({ U("root"), U("other_receivers") }); - value property_value_holders = value::array(); - value members = value::array(); + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("other_receivers") }); + auto property_value_holders = value::array(); + auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false)); - value target_role_path = value_of({ U("root"), U("receivers") }); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); + const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; - web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); - value object_properties_set_validation = output.as_array().at(0); + const auto object_properties_set_validation = output.as_array().at(0); - BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); BST_CHECK_EQUAL(nmos::nc_restore_validation_status::not_found, nmos::fields::nc::status(object_properties_set_validation)); BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); @@ -577,32 +577,32 @@ BST_TEST_CASE(testApplyBackupDataSet) } { // Mixture of filter_property_value_holders_handler and errors in Rebuild mode - // + // filter_property_value_holders_called = false; modify_rebuildable_block_called = false; // Create Object Properties Holder - value object_properties_holders = value::array(); - value role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - value property_value_holders = value::array(); - nmos::nc_property_id property_id(2, 1); + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const nmos::nc_property_id property_id(2, 1); // This is a read only property push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value"))); //read only push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcString"), false, false)); // error in data type - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); // must be a more efficient way of initializing these role paths - value target_role_path = value_of({ U("root"), U("receivers") }); + const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; - web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; bool validate = true; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); - value object_properties_set_validation = output.as_array().at(0); + const auto object_properties_set_validation = output.as_array().at(0); const auto& property_restore_notices = nmos::fields::nc::notices(object_properties_set_validation); // make sure the validation status propagates from the callback @@ -616,37 +616,37 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); - // expecting callback to filter_property_value_holders_called + // expecting callback to filter_property_value_holders_called // but not to modify_rebuildable_block_called BST_CHECK(filter_property_value_holders_called); BST_CHECK(!modify_rebuildable_block_called); } { // Incorrect property name in property value holders - // + // filter_property_value_holders_called = false; modify_rebuildable_block_called = false; // Create Object Properties Holder - value object_properties_holders = value::array(); - value role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - value property_value_holders = value::array(); + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); // This is a read only property push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("wrong_property_name"), U("NcString"), false, value("change this value"))); //read only - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); // must be a more efficient way of initializing these role paths - value target_role_path = value_of({ U("root"), U("receivers") }); + const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; - web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; bool validate = true; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); - value object_properties_set_validation = output.as_array().at(0); + const auto object_properties_set_validation = output.as_array().at(0); const auto& property_restore_notices = nmos::fields::nc::notices(object_properties_set_validation); // make sure the validation status propagates from the callback @@ -660,37 +660,37 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); - // expecting callback to filter_property_value_holders_called + // expecting callback to filter_property_value_holders_called // but not to modify_rebuildable_block_called BST_CHECK(!filter_property_value_holders_called); BST_CHECK(!modify_rebuildable_block_called); } { // Incorrect property type in property value holders - // + // filter_property_value_holders_called = false; modify_rebuildable_block_called = false; // Create Object Properties Holder - value object_properties_holders = value::array(); - value role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - value property_value_holders = value::array(); + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); // This is a read only property push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("wrong_data_type"), false, value("change this value"))); //read only - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); // must be a more efficient way of initializing these role paths - value target_role_path = value_of({ U("root"), U("receivers") }); + const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; - web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; bool validate = true; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); - value object_properties_set_validation = output.as_array().at(0); + const auto object_properties_set_validation = output.as_array().at(0); const auto& property_restore_notices = nmos::fields::nc::notices(object_properties_set_validation); // make sure the validation status propagates from the callback @@ -704,7 +704,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); - // expecting callback to filter_property_value_holders_called + // expecting callback to filter_property_value_holders_called // but not to modify_rebuildable_block_called BST_CHECK(!filter_property_value_holders_called); BST_CHECK(!modify_rebuildable_block_called); @@ -725,18 +725,18 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) // Create Device Model // root auto root_block = nmos::make_root_block(); - nmos::nc_oid oid = nmos::root_block_oid; + auto oid = nmos::root_block_oid; // root, ClassManager auto class_manager = nmos::make_class_manager(++oid, control_protocol_state); - nmos::nc_oid receiver_block_oid = ++oid; + const auto receiver_block_oid = ++oid; // root, receivers auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); nmos::make_rebuildable(receivers); // root, receivers, mon1 auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); nmos::make_rebuildable(monitor1); - nmos::nc_oid monitor_1_oid = oid; - nmos::nc_class_id monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + auto monitor_1_oid = oid; + const auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); nmos::push_back(receivers, monitor1); @@ -760,26 +760,26 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) // Check that Modify mode is unaffected by undefined Rebuild mode callbacks // // Create Object Properties Holder - value object_properties_holders = value::array(); - value role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - value property_value_holders = value::array(); - value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false); + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false); push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); push_back(object_properties_holders, object_properties_holder); - value target_role_path = value_of({ U("root"), U("receivers") }); + const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; - web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::modify; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); - value object_properties_set_validation = output.as_array().at(0); + const auto object_properties_set_validation = output.as_array().at(0); - BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); } @@ -787,55 +787,55 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) // Check that Rebuild mode is unaffected by undefined Rebuild mode callbacks when no objects are being rebuilt // // Create Object Properties Holder - value object_properties_holders = value::array(); - value role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - value property_value_holders = value::array(); - value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false); + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false); push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); push_back(object_properties_holders, object_properties_holder); - value target_role_path = value_of({ U("root"), U("receivers") }); + const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; - web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); - value object_properties_set_validation = output.as_array().at(0); + const auto object_properties_set_validation = output.as_array().at(0); - BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); } { // Check undefined filter_property_value_holders_handler causes an unsupported mode error when attempting to modify a read only property in Rebuild mode - // + // // Create Object Properties Holder - value object_properties_holders = value::array(); - value role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - value property_value_holders = value::array(); - nmos::nc_property_id property_id(2, 1); + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const nmos::nc_property_id property_id(2, 1); // This is a read only property - value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); push_back(property_value_holders, property_value_holder); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths - value target_role_path = value_of({ U("root"), U("receivers") }); + const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; - web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; bool validate = true; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); - value object_properties_set_validation = output.as_array().at(0); + const auto object_properties_set_validation = output.as_array().at(0); // make sure the validation status propagates from the callback BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); @@ -844,27 +844,27 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) // Check undefined modify_rebuildable_block_handler causes an unsupported error when attempting to modify a rebuildable block // // Create Object Properties Holder - value object_properties_holders = value::array(); - value role_path = value_of({ U("root"), U("receivers") }); - value property_value_holders = value::array(); - value members = value::array(); + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers") }); + auto property_value_holders = value::array(); + auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, false)); - value target_role_path = value_of({ U("root"), U("receivers") }); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); + const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; - web::json::value restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path); - value output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); - value object_properties_set_validation = output.as_array().at(0); + const auto object_properties_set_validation = output.as_array().at(0); - BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); } } \ No newline at end of file From eeac4e08c6dda98f273fae2448140a45e9e62a68 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 13 Jan 2025 13:56:46 +0000 Subject: [PATCH 160/250] Change value to array --- Development/nmos/configuration_handlers.h | 2 +- Development/nmos/configuration_methods.cpp | 2 +- Development/nmos/configuration_utils.cpp | 6 +++--- Development/nmos/configuration_utils.h | 2 +- Development/nmos/control_protocol_utils.cpp | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h index 8d8c58eb4..19ee9b9d0 100644 --- a/Development/nmos/configuration_handlers.h +++ b/Development/nmos/configuration_handlers.h @@ -24,7 +24,7 @@ namespace nmos // This callback is invoked if attempting to modify a rebuildable block when restoring a configuration. // This function should handle the modification of the Device Model and any corresponding NMOS resources // and return correpsonding NcObjectPropertiesSetValidation objects for each object modified/added - typedef std::function modify_rebuildable_block_handler; + typedef std::function modify_rebuildable_block_handler; } #endif diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index 23e42a437..c9e35d60b 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -45,7 +45,7 @@ namespace nmos // Get property_value_holders for this resource const auto property_value_holders = make_property_value_holders(resource, get_control_protocol_class_descriptor).as_array(); - const auto role_path = get_role_path(resources, resource).as_array(); + 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, nmos::fields::nc::is_rebuildable(resource.data)); diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 683626dbc..550320a0c 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -291,7 +291,7 @@ namespace nmos return object_properties_set_validation_values; } - web::json::value get_role_path(const nmos::resources& resources, const nmos::resource& resource) + web::json::array 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? @@ -318,14 +318,14 @@ namespace nmos std::reverse(role_path.as_array().begin(), role_path.as_array().end()); - return role_path; + return role_path.as_array(); } web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) { auto object_properties_set_validation_values = web::json::value::array(); - const auto target_role_path = get_role_path(resources, resource).as_array(); + const auto target_role_path = get_role_path(resources, resource); // Detect and warn if there are any object_properties_holders outside of the target role path's scope // Hmmm, can this be done as a one step process rather than filtering and then iterating over filtered list? diff --git a/Development/nmos/configuration_utils.h b/Development/nmos/configuration_utils.h index a026b1e37..3ff22f785 100644 --- a/Development/nmos/configuration_utils.h +++ b/Development/nmos/configuration_utils.h @@ -15,7 +15,7 @@ namespace nmos bool is_block_modified(const nmos::resource& resource, const web::json::value& object_properties_holder); // Get role path of resource given the Device Model resources - web::json::value get_role_path(const nmos::resources& resources, const nmos::resource& resource); + web::json::array get_role_path(const nmos::resources& resources, const nmos::resource& resource); web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); } diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 8bb52928e..33284ca46 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -337,7 +337,7 @@ namespace nmos constraints_validation(data, value::null(), property_constraints, params); } - web::json::value get_nc_block_member_descriptor(const resources& resources, const nmos::resource& parent_nc_block_resource, web::json::value& role_path_segments) + web::json::value get_nc_block_member_descriptor(const resources& resources, const nmos::resource& parent_nc_block_resource, web::json::array& role_path_segments) { if (parent_nc_block_resource.data.has_field(nmos::fields::nc::members)) { From faa05cdd3dbc99e79b0155c01ba3457dcaa21b00 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 13 Jan 2025 14:00:58 +0000 Subject: [PATCH 161/250] Remove redundant helper function --- Development/nmos/control_protocol_utils.cpp | 43 +++++++++------------ 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 33284ca46..6e3bac9d7 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -374,30 +374,6 @@ namespace nmos } return web::json::value{}; } - - - // generic find control class property descriptor in property_descriptor_ array (NcPropertyDescriptor) - web::json::value find_property_descriptor(const nc_property_id& property_id, const nc_class_id& class_id_, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) - { - using web::json::value; - - auto class_id = class_id_; - - while (!class_id.empty()) - { - const auto& control_class = get_control_protocol_class_descriptor(class_id); - const auto& property_descriptors = control_class.property_descriptors.as_array(); - auto found = std::find_if(property_descriptors.begin(), property_descriptors.end(), [&property_id](const web::json::value& property_descriptor) - { - return (property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor))); - }); - if (property_descriptors.end() != found) { return *found; } - - class_id.pop_back(); - } - - return value::null(); - } } // is the given class_id a NcBlock @@ -446,7 +422,24 @@ namespace nmos // find control class property descriptor (NcPropertyDescriptor) web::json::value find_property_descriptor(const nc_property_id& property_id, const nc_class_id& class_id_, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { - return details::find_property_descriptor(property_id, class_id_, get_control_protocol_class_descriptor); + using web::json::value; + + auto class_id = class_id_; + + while (!class_id.empty()) + { + const auto& control_class = get_control_protocol_class_descriptor(class_id); + const auto& property_descriptors = control_class.property_descriptors.as_array(); + auto found = std::find_if(property_descriptors.begin(), property_descriptors.end(), [&property_id](const web::json::value& property_descriptor) + { + return (property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor))); + }); + if (property_descriptors.end() != found) { return *found; } + + class_id.pop_back(); + } + + return value::null(); } // get block member descriptors From acde2d469f2da297ee4f93d32306fa9cf912f88c Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 13 Jan 2025 14:03:08 +0000 Subject: [PATCH 162/250] Iterate example modify_rebuildable_block handler --- .../nmos-cpp-node/node_implementation.cpp | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index cf10748ea..788319c03 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1773,7 +1773,7 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo // Validate the object_properties_holder // Find object_properties_holder for resource - const auto& filtered_holders = boost::copy_range>(object_properties_holders.as_array() + const auto& filtered_holders = boost::copy_range>(object_properties_holders | boost::adaptors::filtered([&resources, &resource](const web::json::value& object_properties_holder) { return nmos::fields::nc::path(object_properties_holder) == nmos::get_role_path(resources, resource); @@ -1911,27 +1911,23 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo const auto& found2 = nmos::find_resource(resources, touchpoint_uuid.as_string()); const auto& device_id = nmos::fields::device_id(found2->data); - const auto& interface_bindings = nmos::fields::interface_bindings(found2->data); - const auto& video_type = nmos::fields::format(found2->data); // calculate child resource role path - const auto oid = nmos::fields::nc::oid(restore_member); - - const auto& target_role_path = nmos::get_role_path(resources, resource); + const auto& target_role_path_ = nmos::get_role_path(resources, resource); // Hmmmm, there must be a better way of appending the child role to the end of the target role path array... auto child_role_path = web::json::value::array(); - for (const auto& path_element : target_role_path.as_array()) + for (const auto& path_element : target_role_path_) { web::json::push_back(child_role_path, path_element); } web::json::push_back(child_role_path, nmos::fields::nc::role(restore_member)); // Find the object_properties_holder that describes the new receiver monitor - const auto& filtered_child_object_properties_holders = boost::copy_range>(object_properties_holders.as_array() + const auto& filtered_child_object_properties_holders = boost::copy_range>(object_properties_holders | boost::adaptors::filtered([&child_role_path](const web::json::value& object_properties_holder) { - return nmos::fields::nc::path(object_properties_holder) == child_role_path; + return nmos::fields::nc::path(object_properties_holder) == child_role_path.as_array(); }) ); @@ -1957,11 +1953,11 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo } const auto& oid_property_holder = *block_members_properties_holders.begin(); - const auto& oid = nmos::fields::nc::value(oid_property_holder); + const auto& oid2 = nmos::fields::nc::value(oid_property_holder); - auto found3 = nmos::find_resource_if(resources, nmos::types::nc_receiver_monitor, [&oid](const nmos::resource& resource) + auto found3 = nmos::find_resource_if(resources, nmos::types::nc_receiver_monitor, [&oid2](const nmos::resource& resource) { - return oid == nmos::fields::nc::oid(resource.data); + return oid2 == nmos::fields::nc::oid(resource.data); }); const auto& touchpoints2 = found3->data.at(nmos::fields::nc::touchpoints); @@ -2010,8 +2006,7 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo // Hmmmmmmmmmmmmmmmmmmmmmmmmmmmmm // create some helper functions to do things like: - // - find an nmos resource assosiated with a control protocol resource (via touchpoint) - // - find an nmos resource assosiated with a control protocol resource (via touchpoint) + // - find an nmos resource associated with a control protocol resource (via touchpoint) // - manipulate the object_properties_holder to create a json resource based on the object_properties_holders so don't have to keep querying json // - functions to compare device model to object_properties_holder to show differences From 35e221a0b0f219d6f5fc3c40d7f3630029c583ce Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 15 Jan 2025 11:19:23 +0000 Subject: [PATCH 163/250] add nc namespace to control_protocol_utils --- Development/cmake/NmosCppTest.cmake | 1 + .../nmos-cpp-node/node_implementation.cpp | 151 ++- Development/nmos/configuration_api.cpp | 34 +- Development/nmos/configuration_methods.cpp | 4 +- Development/nmos/configuration_utils.cpp | 37 +- Development/nmos/configuration_utils.h | 2 + .../nmos/control_protocol_handlers.cpp | 4 +- Development/nmos/control_protocol_methods.cpp | 34 +- Development/nmos/control_protocol_utils.cpp | 1097 +++++++++-------- Development/nmos/control_protocol_utils.h | 113 +- Development/nmos/control_protocol_ws_api.cpp | 2 +- .../nmos/test/configuration_utils_test.cpp | 64 +- .../nmos/test/control_protocol_test.cpp | 334 ++--- .../nmos/test/control_protocol_utils_test.cpp | 70 ++ 14 files changed, 1028 insertions(+), 919 deletions(-) create mode 100644 Development/nmos/test/control_protocol_utils_test.cpp diff --git a/Development/cmake/NmosCppTest.cmake b/Development/cmake/NmosCppTest.cmake index 9181e1438..2ff493779 100644 --- a/Development/cmake/NmosCppTest.cmake +++ b/Development/cmake/NmosCppTest.cmake @@ -45,6 +45,7 @@ set(NMOS_CPP_TEST_NMOS_TEST_SOURCES nmos/test/channels_test.cpp nmos/test/configuration_utils_test.cpp nmos/test/control_protocol_test.cpp + nmos/test/control_protocol_utils_test.cpp nmos/test/did_sdid_test.cpp nmos/test/event_type_test.cpp nmos/test/json_validator_test.cpp diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 788319c03..684e9288b 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -931,7 +931,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr if (0 <= nmos::fields::control_protocol_ws_port(model.settings)) { // example to create a non-standard Gain control class - const auto gain_control_class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 1 }); + const auto gain_control_class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 1 }); const web::json::field_as_number gain_value{ U("gainValue") }; { // Gain control class property descriptors @@ -953,7 +953,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr }; // example to create a non-standard Example control class - const auto example_control_class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 2 }); + const auto example_control_class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 2 }); const web::json::field_as_number enum_property{ U("enumProperty") }; const web::json::field_as_string string_property{ U("stringProperty") }; const web::json::field_as_number number_property{ U("numberProperty") }; @@ -1172,7 +1172,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr }; // example to create a non-standard Temperature Sensor control class - const auto temperature_sensor_control_class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 3 }); + const auto temperature_sensor_control_class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 3 }); const web::json::field_as_number temperature{ U("temperature") }; const web::json::field_as_string unit{ U("uint") }; { @@ -1223,14 +1223,14 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr auto left_gain = make_gain_control(++oid, channel_gain_oid, U("left-gain"), U("Left gain"), U("Left channel gain"), value::null(), value::null(), 0.0); auto right_gain = make_gain_control(++oid, channel_gain_oid, U("right-gain"), U("Right gain"), U("Right channel gain"), value::null(), value::null(), 0.0); // add left-gain and right-gain to channel gain - nmos::push_back(channel_gain, left_gain); - nmos::push_back(channel_gain, right_gain); + nmos::nc::push_back(channel_gain, left_gain); + nmos::nc::push_back(channel_gain, right_gain); // example master-gain auto master_gain = make_gain_control(++oid, channel_gain_oid, U("master-gain"), U("Master gain"), U("Master gain block"), value::null(), value::null(), 0.0); // add channel-gain and master-gain to stereo-gain - nmos::push_back(stereo_gain, channel_gain); - nmos::push_back(stereo_gain, master_gain); + nmos::nc::push_back(stereo_gain, channel_gain); + nmos::nc::push_back(stereo_gain, master_gain); // example example-control auto example_control = make_example_control(++oid, nmos::root_block_oid, U("ExampleControl"), U("Example control worker"), U("Example control worker"), @@ -1276,7 +1276,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const auto receiver_monitor = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, receiver_id}) } })); // add receiver-monitor to root-block - nmos::push_back(receiver_block, receiver_monitor); + nmos::nc::push_back(receiver_block, receiver_monitor); } } } @@ -1285,19 +1285,19 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const auto temperature_sensor = make_temperature_sensor(++oid, nmos::root_block_oid, U("temperature-sensor"), U("Temperature Sensor"), U("Temperature Sensor block"), value::null(), value::null(), 0.0, U("Celsius")); // add receiver monitor block - nmos::push_back(root_block, receiver_block); + nmos::nc::push_back(root_block, receiver_block); // add temperature-sensor to root-block - nmos::push_back(root_block, temperature_sensor); + nmos::nc::push_back(root_block, temperature_sensor); // add example-control to root-block - nmos::push_back(root_block, example_control); + nmos::nc::push_back(root_block, example_control); // add stereo-gain to root-block - nmos::push_back(root_block, stereo_gain); + nmos::nc::push_back(root_block, stereo_gain); // add class-manager to root-block - nmos::push_back(root_block, class_manager); + nmos::nc::push_back(root_block, class_manager); // add device-manager to root-block - nmos::push_back(root_block, device_manager); + nmos::nc::push_back(root_block, device_manager); // add bulk-properties-manager to root-block - nmos::push_back(root_block, bulk_properties_manager); + nmos::nc::push_back(root_block, bulk_properties_manager); // insert control protocol resources to model insert_root_after(delay_millis, root_block, gate); @@ -1367,7 +1367,7 @@ void node_implementation_run(nmos::node_model& model, slog::base_gate& gate) // update temperature sensor { - const auto temperature_sensor_control_class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 3 }); + const auto temperature_sensor_control_class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 3 }); const web::json::field_as_number temperature{ U("temperature") }; auto& resources = model.control_protocol_resources; @@ -1384,7 +1384,7 @@ void node_implementation_run(nmos::node_model& model, slog::base_gate& gate) { {3, 1}, nmos::nc_property_change_type::type::value_changed, web::json::value(temp.scaled_value()) } }); - nmos::modify_control_protocol_resource(model.control_protocol_resources, found->id, [&](nmos::resource& resource) + nmos::nc::modify_resource(model.control_protocol_resources, found->id, [&](nmos::resource& resource) { resource.data[temperature] = temp.scaled_value(); @@ -1743,7 +1743,7 @@ nmos::filter_property_value_holders_handler make_filter_property_value_holders_h for (const auto& property_value : property_values) { const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); - const auto& property_descriptor = nmos::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); + const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); // In this example we are only allowing writable properties to be modified if (bool(nmos::fields::nc::is_read_only(property_descriptor))) @@ -1790,7 +1790,7 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo const auto& object_properties_holder = *filtered_holders.begin(); const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); - if (!nmos::is_nc_block(class_id)) + if (!nmos::nc::is_block(class_id)) { // Error return web::json::value::array(); @@ -1813,6 +1813,7 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo const auto& reference_members = nmos::fields::nc::members(resource.data); std::vector members_to_remove; + std::vector members_to_add; // Iterate through the members of the block and compare to the members in the backup dataset for (const auto& reference_member : reference_members) @@ -1827,24 +1828,14 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo { // can't find this oid in restore dataset, so member has been removed // get the receiver monitor resource - auto found = nmos::find_resource_if(resources, nmos::types::nc_receiver_monitor, [&reference_member](const nmos::resource& resource) - { - return nmos::fields::nc::oid(reference_member) == nmos::fields::nc::oid(resource.data); - }); - - // use the touchpoint UUID to idenity the receiver resource linked to the receiver resource - const auto& touchpoints = found->data.at(nmos::fields::nc::touchpoints); - if (touchpoints.size() == 0) - { - // Error - continue; - } + auto found = nmos::find_resource(resources, utility::conversions::details::to_string_t(nmos::fields::nc::oid(reference_member))); - // erase receiver NMOS resource and receiver monitor Device Model resource - const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); + const auto& touchpoint_resource = nmos::nc::find_touchpoint_resource(model.node_resources, *found); + + if (touchpoint_resource != resources.end()) { const auto lock = model.write_lock(); - bool success = erase_resource(model.node_resources, touchpoint_uuid.as_string()); + bool success = erase_resource(model.node_resources, nmos::fields::id(touchpoint_resource->data)); if (!success) { @@ -1894,23 +1885,14 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo continue; } const auto& example_monitor = *reference_members.begin(); - auto found = nmos::find_resource_if(resources, nmos::types::nc_receiver_monitor, [&example_monitor](const nmos::resource& resource) - { - return nmos::fields::nc::oid(example_monitor) == nmos::fields::nc::oid(resource.data); - }); - - const auto& touchpoints = found->data.at(nmos::fields::nc::touchpoints); - - if (touchpoints.size() == 0) + const auto& found = nmos::find_resource(resources, utility::conversions::details::to_string_t(nmos::fields::nc::oid(example_monitor))); + const auto& touchpoint_resource = nmos::nc::find_touchpoint_resource(model.node_resources, *found); + if (touchpoint_resource == resources.end()) { // Error continue; } - - const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); - const auto& found2 = nmos::find_resource(resources, touchpoint_uuid.as_string()); - - const auto& device_id = nmos::fields::device_id(found2->data); + const auto& device_id = nmos::fields::device_id(touchpoint_resource->data); // calculate child resource role path const auto& target_role_path_ = nmos::get_role_path(resources, resource); @@ -1939,39 +1921,33 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo const auto& child_object_properties_holder = *filtered_child_object_properties_holders.begin(); - const auto& oid_properties_holders = boost::copy_range>(nmos::fields::nc::values(child_object_properties_holder) - | boost::adaptors::filtered([](const web::json::value& property_value_holder) - { - return nmos::nc_property_id(1, 2) == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value_holder)); - }) - ); - // There should only be a single property holder for the members - if (oid_properties_holders.size() != 1) + const auto& oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_property_id(1, 2)); + + if (oid_property_holder == web::json::value::null()) { // Error - return web::json::value::array(); + continue; } - const auto& oid_property_holder = *block_members_properties_holders.begin(); - const auto& oid2 = nmos::fields::nc::value(oid_property_holder); + const auto& touchpoint_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_property_id(1, 7)); - auto found3 = nmos::find_resource_if(resources, nmos::types::nc_receiver_monitor, [&oid2](const nmos::resource& resource) - { - return oid2 == nmos::fields::nc::oid(resource.data); - }); + if (touchpoint_property_holder == web::json::value::null()) + { + // Error + continue; + } + const auto& oid2 = nmos::fields::nc::value(oid_property_holder); - const auto& touchpoints2 = found3->data.at(nmos::fields::nc::touchpoints); + const auto& touchpoints = nmos::fields::nc::value(touchpoint_property_holder); - if (touchpoints2.size() == 0) + if (touchpoints.size() != 1) { // Error continue; } - - const auto& touchpoint_uuid2 = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints2.as_array().begin())); + const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); // Make resources - // const auto host_interfaces = nmos::get_host_interfaces(model.settings); const auto& host_address = nmos::fields::host_address(model.settings); // the interface corresponding to the host address is used for the example node's WebSocket senders and receivers @@ -1981,7 +1957,6 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo slog::log(gate, SLOG_FLF) << "No network interface corresponding to host_address?"; throw node_implementation_init_exception(); } - const auto& host_interface = *host_interface_; const auto& primary_address = model.settings.has_field(nmos::fields::host_addresses) ? web::json::front(nmos::fields::host_addresses(model.settings)).as_string() : host_address; const auto& secondary_address = model.settings.has_field(nmos::fields::host_addresses) ? web::json::back(nmos::fields::host_addresses(model.settings)).as_string() : host_address; @@ -1999,25 +1974,43 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo ? std::vector{ primary_interface.name, secondary_interface.name } : std::vector{ primary_interface.name }; - const auto& receiver = nmos::make_receiver(touchpoint_uuid2.as_string(), device_id, nmos::transports::rtp, interface_names, model.settings); + auto receiver = nmos::make_receiver(touchpoint_uuid.as_string(), device_id, nmos::transports::rtp, interface_names, model.settings); + + const auto& owner_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_property_id(1, 4)); + if (owner_property_holder == web::json::value::null()) + { + // Error + continue; + } + + const auto& role_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_property_id(1, 5)); + if (role_property_holder == web::json::value::null()) + { + // Error + continue; + } + + const auto& owner = nmos::fields::nc::value(owner_property_holder).as_integer(); + const auto& role = nmos::fields::nc::value(role_property_holder).as_string(); - // Create receiver monitor + auto receiver_monitor = nmos::make_receiver_monitor(oid2.as_integer(), true, owner, role, U(""), U(""), web::json::value_of({{nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})}})); + + auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U(""), role, oid2.as_integer(), true, nmos::nc_receiver_monitor_class_id, U(""), owner); + members_to_add.push_back(block_member_descriptor); + // insert resources + insert_resource(model.node_resources, std::move(receiver)); + insert_resource(resources, std::move(receiver_monitor)); // Hmmmmmmmmmmmmmmmmmmmmmmmmmmmmm // create some helper functions to do things like: - // - find an nmos resource associated with a control protocol resource (via touchpoint) // - manipulate the object_properties_holder to create a json resource based on the object_properties_holders so don't have to keep querying json // - functions to compare device model to object_properties_holder to show differences - - //const auto receiver_monitor = nmos::make_receiver_monitor(oid, true, receiver_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, receiver_id}) } })); - - } } // Update the members of the receivers block - if (members_to_remove.size() > 0) + if (members_to_remove.size() > 0 || members_to_add.size() > 0) { auto modified_members = web::json::value::array(); @@ -2034,8 +2027,12 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo web::json::push_back(modified_members, member); } } + for (const auto& member : members_to_add) + { + web::json::push_back(modified_members, member); + } - modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource) + nmos::nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) { resource.data[nmos::fields::nc::members] = modified_members; diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 9aacb6730..7138298d4 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -70,7 +70,7 @@ namespace nmos role_paths.insert(role_path + U("/")); // get members on all NcBlock(s) - if (nmos::is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + if (nmos::nc::is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(member); @@ -195,7 +195,7 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); + const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { @@ -218,7 +218,7 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); + const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { std::set properties_routes; @@ -258,7 +258,7 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); + const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { std::set methods_routes; @@ -305,7 +305,7 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); + const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); @@ -362,11 +362,11 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); + const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { // find the relevant nc_property_descriptor - const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); + const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); @@ -394,11 +394,11 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); + const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { // find the relevant nc_property_descriptor - const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); + const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); @@ -427,7 +427,7 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); + const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { auto arguments = value_of({ @@ -467,7 +467,7 @@ namespace nmos auto& resources = model.control_protocol_resources; auto& arguments = nmos::fields::nc::arguments(body); - const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); + const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { auto method = get_control_protocol_method_descriptor(details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), details::parse_formatted_method_id(method_id)); @@ -481,7 +481,7 @@ namespace nmos try { // do method arguments constraints validation - method_parameters_contraints_validation(arguments, nc_method_descriptor, get_control_protocol_datatype_descriptor); + nc::method_parameters_contraints_validation(arguments, nc_method_descriptor, get_control_protocol_datatype_descriptor); // execute the relevant control method handler, then accumulating up their response to reponses method_result = control_method_handler(resources, *resource, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), gate); @@ -542,11 +542,11 @@ namespace nmos auto& resources = model.control_protocol_resources; - const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); + const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { // find the relevant nc_property_descriptor - const auto& property_descriptor = find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); + const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); @@ -583,7 +583,7 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); + const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { @@ -630,7 +630,7 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); + const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { return details::extract_json(req, gate_).then([res, resources, resource, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, version, &gate_](value body) mutable @@ -687,7 +687,7 @@ namespace nmos auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - const auto& resource = find_control_protocol_resource_by_role_path(resources, role_path); + const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { return details::extract_json(req, gate_).then([res, &resources, resource, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, version, &model, &gate_](value body) mutable diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index c9e35d60b..e0631c55e 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -52,7 +52,7 @@ namespace nmos web::json::push_back(object_properties_holders, object_properties_holder); // 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 (recurse && nmos::nc::is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)))) { if (resource.data.has_field(nmos::fields::nc::members)) { @@ -82,7 +82,7 @@ namespace nmos 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 (nmos::nc::is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)))) { if (resource.data.has_field(nmos::fields::nc::members)) { diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 550320a0c..5031a01c0 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -60,7 +60,7 @@ namespace nmos for (const auto& property_value : property_values) { const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); - const auto& property_descriptor = nmos::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); + const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); if (bool(nmos::fields::nc::is_read_only(property_descriptor))) { @@ -93,7 +93,7 @@ namespace nmos { // Are they blocks? nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); - if (!nmos::is_nc_block(class_id)) + if (!nmos::nc::is_block(class_id)) { return false; } @@ -180,7 +180,7 @@ namespace nmos const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); - if (nmos::is_nc_block(class_id)) + if (nmos::nc::is_block(class_id)) { // if rebuildable and the block has changed then callback if (nmos::fields::nc::is_rebuildable(resource.data) && target_object_properties_holders.size() && is_block_modified(resource, *target_object_properties_holders.begin())) @@ -210,12 +210,7 @@ namespace nmos if (resources.end() != child) { // Append the role of the child to the target role path to create the child role path - // Hmmmm, there must be a better way of appending the child role to the end of the target role path array... - auto child_role_path = web::json::value::array(); - for (const auto& path_element : target_role_path) - { - web::json::push_back(child_role_path, path_element); - } + auto child_role_path = web::json::value_from_elements(target_role_path); web::json::push_back(child_role_path, nmos::fields::nc::role(child->data)); web::json::value child_object_properties_set_validation_values = modify_device_model(resources, *child, child_role_path.as_array(), child_object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); @@ -237,7 +232,7 @@ namespace nmos | boost::adaptors::filtered([&property_restore_notices, &resource, class_id, get_control_protocol_class_descriptor, restore_mode](const web::json::value& property_value) { const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); - const auto& property_descriptor = nmos::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); + const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); return resource.data.at(nmos::fields::nc::name(property_descriptor)) != nmos::fields::nc::value(property_value) && details::is_property_value_valid(property_restore_notices, property_value, property_descriptor, restore_mode, bool(nmos::fields::nc::is_rebuildable(resource.data))); @@ -270,14 +265,14 @@ namespace nmos { const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); - // hmmm, ideally we would pass the value into modify_control_protocol_resource with the validate + // hmmm, ideally we would pass the value into modify_resource with the validate // flag, so that it's subject to property contraints and also the application code can decide if it's a legal value if (!validate) { // modify control protocol resources const auto& value = nmos::fields::nc::value(property_value); - modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource_) + nc::modify_resource(resources, resource.id, [&](nmos::resource& resource_) { resource_.data[nmos::fields::nc::name(property_value)] = value; @@ -350,4 +345,22 @@ namespace nmos return object_properties_set_validation_values; } + + web::json::value get_property_value_holder(const web::json::value& object_properties_holder, const nmos::nc_property_id& property_id) + { + const auto& filtered_property_value_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) + | boost::adaptors::filtered([&property_id](const web::json::value& property_value_holder) + { + return property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value_holder)); + }) + ); + // There should only be a single property holder for the members + if (filtered_property_value_holders.size() != 1) + { + // Error + return web::json::value::null(); + } + + return *filtered_property_value_holders.begin(); + } } diff --git a/Development/nmos/configuration_utils.h b/Development/nmos/configuration_utils.h index 3ff22f785..f0b552b0c 100644 --- a/Development/nmos/configuration_utils.h +++ b/Development/nmos/configuration_utils.h @@ -18,6 +18,8 @@ namespace nmos web::json::array get_role_path(const nmos::resources& resources, const nmos::resource& resource); web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); + + web::json::value get_property_value_holder(const web::json::value& object_properties_holder, const nmos::nc_property_id& property_id); } #endif diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index 522bcc964..8fb51622a 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -74,7 +74,7 @@ namespace nmos { return [&resources](const resource& connection_resource) { - auto found = find_control_protocol_resource(resources, nmos::types::nc_receiver_monitor, connection_resource.id); + auto found = nc::find_resource(resources, nmos::types::nc_receiver_monitor, connection_resource.id); if (resources.end() != found && nc_receiver_monitor_class_id == details::parse_nc_class_id(nmos::fields::nc::class_id(found->data))) { // update receiver-monitor's connectionStatus and payloadStatus properties @@ -91,7 +91,7 @@ namespace nmos { nc_receiver_monitor_payload_status_property_id, nc_property_change_type::type::value_changed, payload_status } }); - modify_control_protocol_resource(resources, found->id, [&](nmos::resource& resource) + nc::modify_resource(resources, found->id, [&](nmos::resource& resource) { resource.data[nmos::fields::nc::connection_status] = connection_status; resource.data[nmos::fields::nc::payload_status] = payload_status; diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index a83c6db58..ed14ee8b7 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -21,7 +21,7 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Get property: " << property_id.serialize(); // find the relevant nc_property_descriptor - const auto& property = find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, resource.data.at(nmos::fields::nc::name(property))); @@ -46,7 +46,7 @@ namespace nmos // find the relevant nc_property_descriptor const auto property_id_ = details::parse_nc_property_id(property_id); - const auto& property = find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (nmos::fields::nc::is_read_only(property)) @@ -70,10 +70,10 @@ namespace nmos try { // do property constraints validation - nmos::details::constraints_validation(val, details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); + nmos::nc::details::constraints_validation(val, nc::details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { nc::details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); // update property - modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource) + nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) { resource.data[nmos::fields::nc::name(property)] = val; @@ -114,7 +114,7 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Get sequence item: " << property_id.serialize() << " index: " << index; // find the relevant nc_property_descriptor - const auto& property = find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { const auto& data = resource.data.at(nmos::fields::nc::name(property)); @@ -159,7 +159,7 @@ namespace nmos // find the relevant nc_property_descriptor const auto property_id_ = details::parse_nc_property_id(property_id); - const auto& property = find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (nmos::fields::nc::is_read_only(property)) @@ -183,10 +183,10 @@ namespace nmos try { // do property constraints validation - nmos::details::constraints_validation(val, details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); + nmos::nc::details::constraints_validation(val, nc::details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { nc::details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); // update property - modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource) + nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) { resource.data[nmos::fields::nc::name(property)][index] = val; @@ -237,7 +237,7 @@ namespace nmos // find the relevant nc_property_descriptor const auto property_id_ = details::parse_nc_property_id(property_id); - const auto& property = find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (nmos::fields::nc::is_read_only(property)) @@ -260,10 +260,10 @@ namespace nmos try { // do property constraints validation - nmos::details::constraints_validation(val, details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); + nmos::nc::details::constraints_validation(val, nc::details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { nc::details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); // update property - modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource) + nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) { auto& sequence = resource.data[nmos::fields::nc::name(property)]; if (data.is_null()) { sequence = value::array(); } @@ -306,7 +306,7 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Remove sequence item: " << property_id.serialize() << " index: " << index; // find the relevant nc_property_descriptor - const auto& property = find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { const auto& data = resource.data.at(nmos::fields::nc::name(property)); @@ -322,7 +322,7 @@ namespace nmos if (data.as_array().size() > (size_t)index) { - modify_control_protocol_resource(resources, resource.id, [&](nmos::resource& resource) + nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) { auto& sequence = resource.data[nmos::fields::nc::name(property)].as_array(); sequence.erase(index); @@ -358,7 +358,7 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Get sequence length: " << property_id.serialize(); // find the relevant nc_property_descriptor - const auto& property = find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (!nmos::fields::nc::is_sequence(property)) @@ -416,7 +416,7 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Get descriptors of members of the block: " << "recurse: " << recurse; auto descriptors = value::array(); - nmos::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); + nmos::nc::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); } @@ -506,7 +506,7 @@ namespace nmos } auto descriptors = value::array(); - nmos::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); + nmos::nc::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); } @@ -533,7 +533,7 @@ namespace nmos // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... auto descriptors = value::array(); - nmos::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); + nmos::nc::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); } diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 6e3bac9d7..26d6b23b5 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -15,717 +15,738 @@ namespace nmos { - namespace details + namespace nc { - bool is_control_class(const nc_class_id& control_class_id, const nc_class_id& class_id_) + namespace details { - nc_class_id class_id{ class_id_ }; - if (control_class_id.size() < class_id.size()) + bool is_control_class(const nc_class_id& control_class_id, const nc_class_id& class_id_) { - // truncate test class_id to relevant class_id - class_id.resize(control_class_id.size()); - } - return control_class_id == class_id; - } - - // get the runtime property constraints of a specific property_id - web::json::value get_runtime_property_constraints(const nc_property_id& property_id, const web::json::value& runtime_property_constraints) - { - using web::json::value; - - if (!runtime_property_constraints.is_null()) - { - auto& runtime_prop_constraints = runtime_property_constraints.as_array(); - auto found_constraints = std::find_if(runtime_prop_constraints.begin(), runtime_prop_constraints.end(), [&property_id](const web::json::value& constraints) - { - return property_id == parse_nc_property_id(nmos::fields::nc::property_id(constraints)); - }); - - if (runtime_prop_constraints.end() != found_constraints) + nc_class_id class_id{ class_id_ }; + if (control_class_id.size() < class_id.size()) { - return *found_constraints; + // truncate test class_id to relevant class_id + class_id.resize(control_class_id.size()); } + return control_class_id == class_id; } - return value::null(); - } - - // get the datatype descriptor of a specific type_name - web::json::value get_datatype_descriptor(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) - { - using web::json::value; - - if (!type_name.is_null()) - { - return get_control_protocol_datatype_descriptor(type_name.as_string()).descriptor; - } - return value::null(); - } - - // get the datatype property constraints of a specific type_name - web::json::value get_datatype_constraints(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) - { - using web::json::value; - // NcDatatypeDescriptor - const auto& datatype_descriptor = get_datatype_descriptor(type_name, get_control_protocol_datatype_descriptor); - if (!datatype_descriptor.is_null()) + // get the runtime property constraints of a specific property_id + web::json::value get_runtime_property_constraints(const nc_property_id& property_id, const web::json::value& runtime_property_constraints) { - return nmos::fields::nc::constraints(datatype_descriptor); - } - return value::null(); - } + using web::json::value; - // constraints validation, may throw nmos::control_protocol_exception - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring - void constraints_validation(const web::json::value& data, const web::json::value& constraints) - { - auto parameter_constraints_validation = [&constraints](const web::json::value& value) - { - // is numeric constraints - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber - if (constraints.has_field(nmos::fields::nc::step) && !nmos::fields::nc::step(constraints).is_null()) + if (!runtime_property_constraints.is_null()) { - if (value.is_null()) { throw control_protocol_exception("value is null"); } - - if (!value.is_integer()) { throw control_protocol_exception("value is not an integer"); } - - const auto step = nmos::fields::nc::step(constraints).as_double(); - if (step <= 0) { throw control_protocol_exception("step is not a positive integer"); } + auto& runtime_prop_constraints = runtime_property_constraints.as_array(); + auto found_constraints = std::find_if(runtime_prop_constraints.begin(), runtime_prop_constraints.end(), [&property_id](const web::json::value& constraints) + { + return property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::property_id(constraints)); + }); - const auto value_double = value.as_double(); - if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) - { - auto min = nmos::fields::nc::minimum(constraints).as_double(); - if (0 != std::fmod(value_double - min, step)) { throw control_protocol_exception("value is not divisible by step"); } - } - else if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) - { - auto max = nmos::fields::nc::maximum(constraints).as_double(); - if (0 != std::fmod(max - value_double, step)) { throw control_protocol_exception("value is not divisible by step"); } - } - else + if (runtime_prop_constraints.end() != found_constraints) { - if (0 != std::fmod(value_double, step)) { throw control_protocol_exception("value is not divisible by step"); } + return *found_constraints; } } - if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) - { - if (value.is_null()) { throw control_protocol_exception("value is null"); } - - if (!value.is_integer() || value.as_double() < nmos::fields::nc::minimum(constraints).as_double()) { throw control_protocol_exception("value is less than minimum"); } - } - if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) - { - if (value.is_null()) { throw control_protocol_exception("value is null"); } - - if (!value.is_integer() || value.as_double() > nmos::fields::nc::maximum(constraints).as_double()) { throw control_protocol_exception("value is greater than maximum"); } - } + return value::null(); + } - // is string constraints - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring - if (constraints.has_field(nmos::fields::nc::max_characters) && !constraints.at(nmos::fields::nc::max_characters).is_null()) - { - if (value.is_null()) { throw control_protocol_exception("value is null"); } + // get the datatype descriptor of a specific type_name + web::json::value get_datatype_descriptor(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) + { + using web::json::value; - const size_t max_characters = nmos::fields::nc::max_characters(constraints); - if (!value.is_string() || value.as_string().length() > max_characters) { throw control_protocol_exception("value is longer than maximum characters"); } - } - if (constraints.has_field(nmos::fields::nc::pattern) && !constraints.at(nmos::fields::nc::pattern).is_null()) + if (!type_name.is_null()) { - if (value.is_null()) { throw control_protocol_exception("value is null"); } - - if (!value.is_string()) { throw control_protocol_exception("value is not a string"); } - const auto value_string = utility::us2s(value.as_string()); - bst::regex pattern(utility::us2s(nmos::fields::nc::pattern(constraints))); - if (!bst::regex_match(value_string, pattern)) { throw control_protocol_exception("value dose not match the pattern"); } + return get_control_protocol_datatype_descriptor(type_name.as_string()).descriptor; } + return value::null(); + } - // reaching here, parameter validation successfully - }; - - if (data.is_array()) + // get the datatype property constraints of a specific type_name + web::json::value get_datatype_constraints(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { - for (const auto& value : data.as_array()) + using web::json::value; + + // NcDatatypeDescriptor + const auto& datatype_descriptor = get_datatype_descriptor(type_name, get_control_protocol_datatype_descriptor); + if (!datatype_descriptor.is_null()) { - parameter_constraints_validation(value); + return nmos::fields::nc::constraints(datatype_descriptor); } + return value::null(); } - else - { - parameter_constraints_validation(data); - } - } - // level 0 datatype constraints validation, may throw nmos::control_protocol_exception - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - void datatype_constraints_validation(const web::json::value& data, const datatype_constraints_validation_parameters& params) - { - auto parameter_constraints_validation = [¶ms](const web::json::value& value_) + // constraints validation, may throw nmos::control_protocol_exception + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring + void constraints_validation(const web::json::value& data, const web::json::value& constraints) { - // no constraints validation required - if (params.datatype_descriptor.is_null()) { return; } + auto parameter_constraints_validation = [&constraints](const web::json::value& value) + { + // is numeric constraints + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber + if (constraints.has_field(nmos::fields::nc::step) && !nmos::fields::nc::step(constraints).is_null()) + { + if (value.is_null()) { throw control_protocol_exception("value is null"); } - const auto& datatype_type = nmos::fields::nc::type(params.datatype_descriptor); + if (!value.is_integer()) { throw control_protocol_exception("value is not an integer"); } - // do NcDatatypeDescriptorPrimitive constraints validation - if (nc_datatype_type::Primitive == datatype_type) - { - // hmm, for the primitive type, it should not have datatype constraints specified via the datatype_descriptor but just in case - const auto& datatype_constraints = nmos::fields::nc::constraints(params.datatype_descriptor); - if (datatype_constraints.is_null()) - { - auto primitive_validation = [](const nc_name& name, const web::json::value& value) + const auto step = nmos::fields::nc::step(constraints).as_double(); + if (step <= 0) { throw control_protocol_exception("step is not a positive integer"); } + + const auto value_double = value.as_double(); + if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) { - auto is_int16 = [](int32_t value) - { - return value >= (std::numeric_limits::min)() - && value <= (std::numeric_limits::max)(); - }; - auto is_uint16 = [](uint32_t value) - { - return value >= (std::numeric_limits::min)() - && value <= (std::numeric_limits::max)(); - }; - auto is_float32 = [](double value) - { - return value >= (std::numeric_limits::lowest)() - && value <= (std::numeric_limits::max)(); - }; + auto min = nmos::fields::nc::minimum(constraints).as_double(); + if (0 != std::fmod(value_double - min, step)) { throw control_protocol_exception("value is not divisible by step"); } + } + else if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) + { + auto max = nmos::fields::nc::maximum(constraints).as_double(); + if (0 != std::fmod(max - value_double, step)) { throw control_protocol_exception("value is not divisible by step"); } + } + else + { + if (0 != std::fmod(value_double, step)) { throw control_protocol_exception("value is not divisible by step"); } + } + } + if (constraints.has_field(nmos::fields::nc::minimum) && !nmos::fields::nc::minimum(constraints).is_null()) + { + if (value.is_null()) { throw control_protocol_exception("value is null"); } - if (U("NcBoolean") == name) { return value.is_boolean(); } - if (U("NcInt16") == name && value.is_number()) { return is_int16(value.as_number().to_int32()); } - if (U("NcInt32") == name && value.is_number()) { return value.as_number().is_int32(); } - if (U("NcInt64") == name && value.is_number()) { return value.as_number().is_int64(); } - if (U("NcUint16") == name && value.is_number()) { return is_uint16(value.as_number().to_uint32()); } - if (U("NcUint32") == name && value.is_number()) { return value.as_number().is_uint32(); } - if (U("NcUint64") == name && value.is_number()) { return value.as_number().is_uint64(); } - if (U("NcFloat32") == name && value.is_number()) { return is_float32(value.as_number().to_double()); } - if (U("NcFloat64") == name && value.is_number()) { return !value.as_number().is_integral(); } - if (U("NcString") == name) { return value.is_string(); } - - // invalid primitive type - return false; - }; - - // do primitive type constraints validation - const auto& name = nmos::fields::nc::name(params.datatype_descriptor); - if (!primitive_validation(name, value_)) + if (!value.is_integer() || value.as_double() < nmos::fields::nc::minimum(constraints).as_double()) { throw control_protocol_exception("value is less than minimum"); } + } + if (constraints.has_field(nmos::fields::nc::maximum) && !nmos::fields::nc::maximum(constraints).is_null()) { - throw control_protocol_exception("value is not a " + utility::us2s(name) + " type");; + if (value.is_null()) { throw control_protocol_exception("value is null"); } + + if (!value.is_integer() || value.as_double() > nmos::fields::nc::maximum(constraints).as_double()) { throw control_protocol_exception("value is greater than maximum"); } } - } - else - { - constraints_validation(value_, datatype_constraints); - } - return; - } + // is string constraints + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring + if (constraints.has_field(nmos::fields::nc::max_characters) && !constraints.at(nmos::fields::nc::max_characters).is_null()) + { + if (value.is_null()) { throw control_protocol_exception("value is null"); } - // do NcDatatypeDescriptorTypeDef constraints validation - if (nc_datatype_type::Typedef == datatype_type) - { - // do the datatype constraints specified via the datatype_descriptor if presented - const auto& datatype_constraints = nmos::fields::nc::constraints(params.datatype_descriptor); - if (datatype_constraints.is_null()) - { - // do parent typename constraints validation - const auto& type_name = params.datatype_descriptor.at(nmos::fields::nc::parent_type); // parent type_name - datatype_constraints_validation(value_, { details::get_datatype_descriptor(type_name, params.get_control_protocol_datatype_descriptor), params.get_control_protocol_datatype_descriptor }); - } - else - { - constraints_validation(value_, datatype_constraints); - } + const size_t max_characters = nmos::fields::nc::max_characters(constraints); + if (!value.is_string() || value.as_string().length() > max_characters) { throw control_protocol_exception("value is longer than maximum characters"); } + } + if (constraints.has_field(nmos::fields::nc::pattern) && !constraints.at(nmos::fields::nc::pattern).is_null()) + { + if (value.is_null()) { throw control_protocol_exception("value is null"); } - return; - } + if (!value.is_string()) { throw control_protocol_exception("value is not a string"); } + const auto value_string = utility::us2s(value.as_string()); + bst::regex pattern(utility::us2s(nmos::fields::nc::pattern(constraints))); + if (!bst::regex_match(value_string, pattern)) { throw control_protocol_exception("value dose not match the pattern"); } + } + + // reaching here, parameter validation successfully + }; - // do NcDatatypeDescriptorEnum constraints validation - if (nc_datatype_type::Enum == datatype_type) + if (data.is_array()) { - const auto& items = nmos::fields::nc::items(params.datatype_descriptor); - if (items.end() == std::find_if(items.begin(), items.end(), [&](const web::json::value& nc_enum_item_descriptor) { return nmos::fields::nc::value(nc_enum_item_descriptor) == value_; })) + for (const auto& value : data.as_array()) { - const auto& name = nmos::fields::nc::name(params.datatype_descriptor); - throw control_protocol_exception("value is not an enum " + utility::us2s(name) + " type"); + parameter_constraints_validation(value); } - - return; } - - // do NcDatatypeDescriptorStruct constraints validation - if (nc_datatype_type::Struct == datatype_type) + else { - const auto& datatype_name = nmos::fields::nc::name(params.datatype_descriptor); - const auto& fields = nmos::fields::nc::fields(params.datatype_descriptor); - // NcFieldDescriptor - for (const web::json::value& nc_field_descriptor : fields) + parameter_constraints_validation(data); + } + } + + // level 0 datatype constraints validation, may throw nmos::control_protocol_exception + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + void datatype_constraints_validation(const web::json::value& data, const datatype_constraints_validation_parameters& params) + { + auto parameter_constraints_validation = [¶ms](const web::json::value& value_) { - const auto& field_name = nmos::fields::nc::name(nc_field_descriptor); - // is field in strurcture - if (!value_.has_field(field_name)) { throw control_protocol_exception("missing " + utility::us2s(field_name) + " in " + utility::us2s(datatype_name)); } + // no constraints validation required + if (params.datatype_descriptor.is_null()) { return; } + + const auto& datatype_type = nmos::fields::nc::type(params.datatype_descriptor); + + // do NcDatatypeDescriptorPrimitive constraints validation + if (nc_datatype_type::Primitive == datatype_type) + { + // hmm, for the primitive type, it should not have datatype constraints specified via the datatype_descriptor but just in case + const auto& datatype_constraints = nmos::fields::nc::constraints(params.datatype_descriptor); + if (datatype_constraints.is_null()) + { + auto primitive_validation = [](const nc_name& name, const web::json::value& value) + { + auto is_int16 = [](int32_t value) + { + return value >= (std::numeric_limits::min)() + && value <= (std::numeric_limits::max)(); + }; + auto is_uint16 = [](uint32_t value) + { + return value >= (std::numeric_limits::min)() + && value <= (std::numeric_limits::max)(); + }; + auto is_float32 = [](double value) + { + return value >= (std::numeric_limits::lowest)() + && value <= (std::numeric_limits::max)(); + }; + + if (U("NcBoolean") == name) { return value.is_boolean(); } + if (U("NcInt16") == name && value.is_number()) { return is_int16(value.as_number().to_int32()); } + if (U("NcInt32") == name && value.is_number()) { return value.as_number().is_int32(); } + if (U("NcInt64") == name && value.is_number()) { return value.as_number().is_int64(); } + if (U("NcUint16") == name && value.is_number()) { return is_uint16(value.as_number().to_uint32()); } + if (U("NcUint32") == name && value.is_number()) { return value.as_number().is_uint32(); } + if (U("NcUint64") == name && value.is_number()) { return value.as_number().is_uint64(); } + if (U("NcFloat32") == name && value.is_number()) { return is_float32(value.as_number().to_double()); } + if (U("NcFloat64") == name && value.is_number()) { return !value.as_number().is_integral(); } + if (U("NcString") == name) { return value.is_string(); } + + // invalid primitive type + return false; + }; - // is field nullable - if (!nmos::fields::nc::is_nullable(nc_field_descriptor) && value_.at(field_name).is_null()) { throw control_protocol_exception(utility::us2s(field_name) + " is not nullable"); } + // do primitive type constraints validation + const auto& name = nmos::fields::nc::name(params.datatype_descriptor); + if (!primitive_validation(name, value_)) + { + throw control_protocol_exception("value is not a " + utility::us2s(name) + " type");; + } + } + else + { + constraints_validation(value_, datatype_constraints); + } - // if field value is null continue to next field - if (value_.at(field_name).is_null()) continue; + return; + } - // is field sequenceable - if (nmos::fields::nc::is_sequence(nc_field_descriptor) != value_.at(field_name).is_array()) { throw control_protocol_exception(utility::us2s(field_name) + " is not sequenceable"); } + // do NcDatatypeDescriptorTypeDef constraints validation + if (nc_datatype_type::Typedef == datatype_type) + { + // do the datatype constraints specified via the datatype_descriptor if presented + const auto& datatype_constraints = nmos::fields::nc::constraints(params.datatype_descriptor); + if (datatype_constraints.is_null()) + { + // do parent typename constraints validation + const auto& type_name = params.datatype_descriptor.at(nmos::fields::nc::parent_type); // parent type_name + datatype_constraints_validation(value_, { details::get_datatype_descriptor(type_name, params.get_control_protocol_datatype_descriptor), params.get_control_protocol_datatype_descriptor }); + } + else + { + constraints_validation(value_, datatype_constraints); + } - // check constraints of its typeName - const auto& field_type_name = nc_field_descriptor.at(nmos::fields::nc::type_name); + return; + } - if (!field_type_name.is_null()) + // do NcDatatypeDescriptorEnum constraints validation + if (nc_datatype_type::Enum == datatype_type) { - auto value = value_.at(field_name); + const auto& items = nmos::fields::nc::items(params.datatype_descriptor); + if (items.end() == std::find_if(items.begin(), items.end(), [&](const web::json::value& nc_enum_item_descriptor) { return nmos::fields::nc::value(nc_enum_item_descriptor) == value_; })) + { + const auto& name = nmos::fields::nc::name(params.datatype_descriptor); + throw control_protocol_exception("value is not an enum " + utility::us2s(name) + " type"); + } - // do typename constraints validation - datatype_constraints_validation(value, { details::get_datatype_descriptor(field_type_name, params.get_control_protocol_datatype_descriptor), params.get_control_protocol_datatype_descriptor }); + return; } - // check against field constraints if present - const auto& constraints = nmos::fields::nc::constraints(nc_field_descriptor); - if (!constraints.is_null()) + // do NcDatatypeDescriptorStruct constraints validation + if (nc_datatype_type::Struct == datatype_type) { - // do field constraints validation - const auto& value = value_.at(field_name); - constraints_validation(value, constraints); + const auto& datatype_name = nmos::fields::nc::name(params.datatype_descriptor); + const auto& fields = nmos::fields::nc::fields(params.datatype_descriptor); + // NcFieldDescriptor + for (const web::json::value& nc_field_descriptor : fields) + { + const auto& field_name = nmos::fields::nc::name(nc_field_descriptor); + // is field in strurcture + if (!value_.has_field(field_name)) { throw control_protocol_exception("missing " + utility::us2s(field_name) + " in " + utility::us2s(datatype_name)); } + + // is field nullable + if (!nmos::fields::nc::is_nullable(nc_field_descriptor) && value_.at(field_name).is_null()) { throw control_protocol_exception(utility::us2s(field_name) + " is not nullable"); } + + // if field value is null continue to next field + if (value_.at(field_name).is_null()) continue; + + // is field sequenceable + if (nmos::fields::nc::is_sequence(nc_field_descriptor) != value_.at(field_name).is_array()) { throw control_protocol_exception(utility::us2s(field_name) + " is not sequenceable"); } + + // check constraints of its typeName + const auto& field_type_name = nc_field_descriptor.at(nmos::fields::nc::type_name); + + if (!field_type_name.is_null()) + { + auto value = value_.at(field_name); + + // do typename constraints validation + datatype_constraints_validation(value, { details::get_datatype_descriptor(field_type_name, params.get_control_protocol_datatype_descriptor), params.get_control_protocol_datatype_descriptor }); + } + + // check against field constraints if present + const auto& constraints = nmos::fields::nc::constraints(nc_field_descriptor); + if (!constraints.is_null()) + { + // do field constraints validation + const auto& value = value_.at(field_name); + constraints_validation(value, constraints); + } + } + // unsupported datatype_type, no validation is required + return; } + }; + + if (data.is_array()) + { + for (const auto& value : data.as_array()) + { + parameter_constraints_validation(value); } - // unsupported datatype_type, no validation is required - return; } - }; - - if (data.is_array()) - { - for (const auto& value : data.as_array()) + else { - parameter_constraints_validation(value); + parameter_constraints_validation(data); } } - else - { - parameter_constraints_validation(data); - } - } - // multiple levels of constraints validation, may throw nmos::control_protocol_exception - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - void constraints_validation(const web::json::value& data, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) - { - // do level 2 runtime property constraints validation - if (!runtime_property_constraints.is_null()) { constraints_validation(data, runtime_property_constraints); return; } + // multiple levels of constraints validation, may throw nmos::control_protocol_exception + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + void constraints_validation(const web::json::value& data, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) + { + // do level 2 runtime property constraints validation + if (!runtime_property_constraints.is_null()) { constraints_validation(data, runtime_property_constraints); return; } - // do level 1 property constraints validation - if (!property_constraints.is_null()) { constraints_validation(data, property_constraints); return; } + // do level 1 property constraints validation + if (!property_constraints.is_null()) { constraints_validation(data, property_constraints); return; } - // do level 0 datatype constraints validation - datatype_constraints_validation(data, params); - } + // do level 0 datatype constraints validation + datatype_constraints_validation(data, params); + } - // method parameter constraints validation, may throw nmos::control_protocol_exception - void method_parameter_constraints_validation(const web::json::value& data, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) - { - using web::json::value; + // method parameter constraints validation, may throw nmos::control_protocol_exception + void method_parameter_constraints_validation(const web::json::value& data, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) + { + using web::json::value; - // do level 1 property constraints & level 0 datatype constraints validation - constraints_validation(data, value::null(), property_constraints, params); - } + // do level 1 property constraints & level 0 datatype constraints validation + constraints_validation(data, value::null(), property_constraints, params); + } - web::json::value get_nc_block_member_descriptor(const resources& resources, const nmos::resource& parent_nc_block_resource, web::json::array& role_path_segments) - { - if (parent_nc_block_resource.data.has_field(nmos::fields::nc::members)) + web::json::value get_nc_block_member_descriptor(const resources& resources, const nmos::resource& parent_nc_block_resource, web::json::array& role_path_segments) { - const auto& members = nmos::fields::nc::members(parent_nc_block_resource.data); + if (parent_nc_block_resource.data.has_field(nmos::fields::nc::members)) + { + const auto& members = nmos::fields::nc::members(parent_nc_block_resource.data); - const auto role_path_segement = web::json::front(role_path_segments); - role_path_segments.erase(0); - // find the role_path_segment member - auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& member) - { - return role_path_segement.as_string() == nmos::fields::nc::role(member); - }); + const auto role_path_segement = web::json::front(role_path_segments); + role_path_segments.erase(0); + // find the role_path_segment member + auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& member) + { + return role_path_segement.as_string() == nmos::fields::nc::role(member); + }); - if (members.end() != member_found) - { - if (role_path_segments.size() == 0) + if (members.end() != member_found) { - // NcBlockMemberDescriptor - return *member_found; - } + if (role_path_segments.size() == 0) + { + // NcBlockMemberDescriptor + return *member_found; + } - // get the role_path_segement member resource - if (is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(*member_found)))) - { - // get resource based on the oid - const auto& oid = nmos::fields::nc::oid(*member_found); - const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found) + // get the role_path_segement member resource + if (is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(*member_found)))) { - return get_nc_block_member_descriptor(resources, *found, role_path_segments); + // get resource based on the oid + const auto& oid = nmos::fields::nc::oid(*member_found); + const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + return get_nc_block_member_descriptor(resources, *found, role_path_segments); + } } } } + return web::json::value{}; } - return web::json::value{}; - } - } - - // is the given class_id a NcBlock - bool is_nc_block(const nc_class_id& class_id) - { - return details::is_control_class(nc_block_class_id, class_id); - } - - // is the given class_id a NcWorker - bool is_nc_worker(const nc_class_id& class_id) - { - return details::is_control_class(nc_worker_class_id, class_id); - } - - // is the given class_id a NcManager - bool is_nc_manager(const nc_class_id& class_id) - { - return details::is_control_class(nc_manager_class_id, class_id); - } - // is the given class_id a NcDeviceManager - bool is_nc_device_manager(const nc_class_id& class_id) - { - return details::is_control_class(nc_device_manager_class_id, class_id); - } + web::json::value parse_role_path(const utility::string_t& role_path_) + { + // tokenize the role_path with the '.' delimiter + std::list role_path_segments; + boost::algorithm::split(role_path_segments, role_path_, [](utility::char_t c) { return '.' == c; }); - // is the given class_id a NcClassManager - bool is_nc_class_manager(const nc_class_id& class_id) - { - return details::is_control_class(nc_class_manager_class_id, class_id); - } + return web::json::value_from_elements(role_path_segments); + } + } - // construct NcClassId - nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const std::vector& suffix) - { - nc_class_id class_id = prefix; - class_id.push_back(authority_key); - class_id.insert(class_id.end(), suffix.begin(), suffix.end()); - return class_id; - } - nc_class_id make_nc_class_id(const nc_class_id& prefix, const std::vector& suffix) - { - return make_nc_class_id(prefix, 0, suffix); - } + // is the given class_id a NcBlock + bool is_block(const nc_class_id& class_id) + { + return details::is_control_class(nc_block_class_id, class_id); + } - // find control class property descriptor (NcPropertyDescriptor) - web::json::value find_property_descriptor(const nc_property_id& property_id, const nc_class_id& class_id_, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) - { - using web::json::value; + // is the given class_id a NcWorker + bool is_worker(const nc_class_id& class_id) + { + return details::is_control_class(nc_worker_class_id, class_id); + } - auto class_id = class_id_; + // is the given class_id a NcManager + bool is_manager(const nc_class_id& class_id) + { + return details::is_control_class(nc_manager_class_id, class_id); + } - while (!class_id.empty()) + // is the given class_id a NcDeviceManager + bool is_device_manager(const nc_class_id& class_id) { - const auto& control_class = get_control_protocol_class_descriptor(class_id); - const auto& property_descriptors = control_class.property_descriptors.as_array(); - auto found = std::find_if(property_descriptors.begin(), property_descriptors.end(), [&property_id](const web::json::value& property_descriptor) - { - return (property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor))); - }); - if (property_descriptors.end() != found) { return *found; } + return details::is_control_class(nc_device_manager_class_id, class_id); + } - class_id.pop_back(); + // is the given class_id a NcClassManager + bool is_class_manager(const nc_class_id& class_id) + { + return details::is_control_class(nc_class_manager_class_id, class_id); } - return value::null(); - } + // construct NcClassId + nc_class_id make_class_id(const nc_class_id& prefix, int32_t authority_key, const std::vector& suffix) + { + nc_class_id class_id = prefix; + class_id.push_back(authority_key); + class_id.insert(class_id.end(), suffix.begin(), suffix.end()); + return class_id; + } + nc_class_id make_class_id(const nc_class_id& prefix, const std::vector& suffix) + { + return make_class_id(prefix, 0, suffix); + } - // get block member descriptors - void get_member_descriptors(const resources& resources, const resource& resource, bool recurse, web::json::array& descriptors) - { - if (resource.data.has_field(nmos::fields::nc::members)) + // find control class property descriptor (NcPropertyDescriptor) + web::json::value find_property_descriptor(const nc_property_id& property_id, const nc_class_id& class_id_, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { - const auto& members = nmos::fields::nc::members(resource.data); + using web::json::value; - for (const auto& member : members) + auto class_id = class_id_; + + while (!class_id.empty()) { - web::json::push_back(descriptors, member); + const auto& control_class = get_control_protocol_class_descriptor(class_id); + const auto& property_descriptors = control_class.property_descriptors.as_array(); + auto found = std::find_if(property_descriptors.begin(), property_descriptors.end(), [&property_id](const web::json::value& property_descriptor) + { + return (property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor))); + }); + if (property_descriptors.end() != found) { return *found; } + + class_id.pop_back(); } - if (recurse) + return value::null(); + } + + // get block member descriptors + void get_member_descriptors(const resources& resources, const resource& resource, bool recurse, web::json::array& descriptors) + { + if (resource.data.has_field(nmos::fields::nc::members)) { - // get members on all NcBlock(s) + const auto& members = nmos::fields::nc::members(resource.data); + for (const auto& member : members) { - if (is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + web::json::push_back(descriptors, member); + } + + if (recurse) + { + // get members on all NcBlock(s) + for (const auto& member : members) { - // get resource based on the oid - const auto& oid = nmos::fields::nc::oid(member); - const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found) + if (is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) { - get_member_descriptors(resources, *found, recurse, descriptors); + // get resource based on the oid + const auto& oid = nmos::fields::nc::oid(member); + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + get_member_descriptors(resources, *found, recurse, descriptors); + } } } } } } - } - // find members with given role name or fragment - void find_members_by_role(const resources& resources, const resource& resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& descriptors) - { - auto find_members_by_matching_role = [&](const web::json::array& members) + // find members with given role name or fragment + void find_members_by_role(const resources& resources, const resource& resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& descriptors) { - using web::json::value; - - auto match = [&](const web::json::value& descriptor) - { - if (match_whole_string) - { - if (case_sensitive) { return role == nmos::fields::nc::role(descriptor); } - else { return boost::algorithm::to_upper_copy(role) == boost::algorithm::to_upper_copy(nmos::fields::nc::role(descriptor)); } - } - else + auto find_members_by_matching_role = [&](const web::json::array& members) { - if (case_sensitive) { return !boost::find_first(nmos::fields::nc::role(descriptor), role).empty(); } - else { return !boost::ifind_first(nmos::fields::nc::role(descriptor), role).empty(); } - } - }; + using web::json::value; - return boost::make_iterator_range(boost::make_filter_iterator(match, members.begin(), members.end()), boost::make_filter_iterator(match, members.end(), members.end())); - }; + auto match = [&](const web::json::value& descriptor) + { + if (match_whole_string) + { + if (case_sensitive) { return role == nmos::fields::nc::role(descriptor); } + else { return boost::algorithm::to_upper_copy(role) == boost::algorithm::to_upper_copy(nmos::fields::nc::role(descriptor)); } + } + else + { + if (case_sensitive) { return !boost::find_first(nmos::fields::nc::role(descriptor), role).empty(); } + else { return !boost::ifind_first(nmos::fields::nc::role(descriptor), role).empty(); } + } + }; - if (resource.data.has_field(nmos::fields::nc::members)) - { - const auto& members = nmos::fields::nc::members(resource.data); + return boost::make_iterator_range(boost::make_filter_iterator(match, members.begin(), members.end()), boost::make_filter_iterator(match, members.end(), members.end())); + }; - auto members_found = find_members_by_matching_role(members); - for (const auto& member : members_found) + if (resource.data.has_field(nmos::fields::nc::members)) { - web::json::push_back(descriptors, member); - } + const auto& members = nmos::fields::nc::members(resource.data); - if (recurse) - { - // do role match on all NcBlock(s) - for (const auto& member : members) + auto members_found = find_members_by_matching_role(members); + for (const auto& member : members_found) { - if (is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + web::json::push_back(descriptors, member); + } + + if (recurse) + { + // do role match on all NcBlock(s) + for (const auto& member : members) { - // get resource based on the oid - const auto& oid = nmos::fields::nc::oid(member); - const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found) + if (is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) { - find_members_by_role(resources, *found, role, match_whole_string, case_sensitive, recurse, descriptors); + // get resource based on the oid + const auto& oid = nmos::fields::nc::oid(member); + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + find_members_by_role(resources, *found, role, match_whole_string, case_sensitive, recurse, descriptors); + } } } } } } - } - // find members with given class id - void find_members_by_class_id(const resources& resources, const nmos::resource& resource, const nc_class_id& class_id_, bool include_derived, bool recurse, web::json::array& descriptors) - { - auto find_members_by_matching_class_id = [&](const web::json::array& members) + // find members with given class id + void find_members_by_class_id(const resources& resources, const nmos::resource& resource, const nc_class_id& class_id_, bool include_derived, bool recurse, web::json::array& descriptors) { - using web::json::value; - - auto match = [&](const web::json::value& descriptor) - { - const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); + auto find_members_by_matching_class_id = [&](const web::json::array& members) + { + using web::json::value; - if (include_derived) { return !boost::find_first(class_id, class_id_).empty(); } - else { return class_id == class_id_; } - }; + auto match = [&](const web::json::value& descriptor) + { + const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); - return boost::make_iterator_range(boost::make_filter_iterator(match, members.begin(), members.end()), boost::make_filter_iterator(match, members.end(), members.end())); - }; + if (include_derived) { return !boost::find_first(class_id, class_id_).empty(); } + else { return class_id == class_id_; } + }; - if (resource.data.has_field(nmos::fields::nc::members)) - { - auto& members = nmos::fields::nc::members(resource.data); + return boost::make_iterator_range(boost::make_filter_iterator(match, members.begin(), members.end()), boost::make_filter_iterator(match, members.end(), members.end())); + }; - auto members_found = find_members_by_matching_class_id(members); - for (const auto& member : members_found) + if (resource.data.has_field(nmos::fields::nc::members)) { - web::json::push_back(descriptors, member); - } + auto& members = nmos::fields::nc::members(resource.data); - if (recurse) - { - // do class_id match on all NcBlock(s) - for (const auto& member : members) + auto members_found = find_members_by_matching_class_id(members); + for (const auto& member : members_found) + { + web::json::push_back(descriptors, member); + } + + if (recurse) { - if (is_nc_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + // do class_id match on all NcBlock(s) + for (const auto& member : members) { - // get resource based on the oid - const auto& oid = nmos::fields::nc::oid(member); - const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found) + if (is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) { - find_members_by_class_id(resources, *found, class_id_, include_derived, recurse, descriptors); + // get resource based on the oid + const auto& oid = nmos::fields::nc::oid(member); + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + find_members_by_class_id(resources, *found, class_id_, include_derived, recurse, descriptors); + } } } } } } - } - // push a control protocol resource into other control protocol NcBlock resource - void push_back(control_protocol_resource& nc_block_resource, const control_protocol_resource& resource) - { - // note, model write lock should aleady be applied by the outer function, so access to control_protocol_resources is OK... + // push a control protocol resource into other control protocol NcBlock resource + void push_back(control_protocol_resource& nc_block_resource, const control_protocol_resource& resource) + { + // note, model write lock should aleady be applied by the outer function, so access to control_protocol_resources is OK... - using web::json::value; + using web::json::value; - auto& parent = nc_block_resource.data; - const auto& child = resource.data; + auto& parent = nc_block_resource.data; + const auto& child = resource.data; - if (!is_nc_block(details::parse_nc_class_id(nmos::fields::nc::class_id(parent)))) throw std::logic_error("non-NcBlock cannot be nested"); + if (!is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(parent)))) throw std::logic_error("non-NcBlock cannot be nested"); - web::json::push_back(parent[nmos::fields::nc::members], - details::make_nc_block_member_descriptor(nmos::fields::description(child), nmos::fields::nc::role(child), nmos::fields::nc::oid(child), nmos::fields::nc::constant_oid(child), details::parse_nc_class_id(nmos::fields::nc::class_id(child)), nmos::fields::nc::user_label(child), nmos::fields::nc::oid(parent))); + web::json::push_back(parent[nmos::fields::nc::members], + nmos::details::make_nc_block_member_descriptor(nmos::fields::description(child), nmos::fields::nc::role(child), nmos::fields::nc::oid(child), nmos::fields::nc::constant_oid(child), nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(child)), nmos::fields::nc::user_label(child), nmos::fields::nc::oid(parent))); - nc_block_resource.resources.push_back(resource); - } + nc_block_resource.resources.push_back(resource); + } - // modify a control protocol resource, and insert notification event to all subscriptions - bool modify_control_protocol_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event) - { - // note, model write lock should aleady be applied by the outer function, so access to control_protocol_resources is OK... + // modify a control protocol resource, and insert notification event to all subscriptions + bool modify_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event) + { + // note, model write lock should aleady be applied by the outer function, so access to control_protocol_resources is OK... - auto found = resources.find(id); - if (resources.end() == found || !found->has_data()) return false; + auto found = resources.find(id); + if (resources.end() == found || !found->has_data()) return false; - auto pre = found->data; + auto pre = found->data; - // "If an exception is thrown by some user-provided operation, then the element pointed to by position is erased." - // This seems too surprising, despite the fact that it means that a modification may have been partially completed, - // so capture and rethrow. - // See https://www.boost.org/doc/libs/1_68_0/libs/multi_index/doc/reference/ord_indices.html#modify - std::exception_ptr modifier_exception; + // "If an exception is thrown by some user-provided operation, then the element pointed to by position is erased." + // This seems too surprising, despite the fact that it means that a modification may have been partially completed, + // so capture and rethrow. + // See https://www.boost.org/doc/libs/1_68_0/libs/multi_index/doc/reference/ord_indices.html#modify + std::exception_ptr modifier_exception; - auto resource_updated = nmos::strictly_increasing_update(resources); - auto result = resources.modify(found, [&resource_updated, &modifier, &modifier_exception](resource& resource) - { - try + auto resource_updated = nmos::strictly_increasing_update(resources); + auto result = resources.modify(found, [&resource_updated, &modifier, &modifier_exception](resource& resource) + { + try + { + modifier(resource); + } + catch (...) + { + modifier_exception = std::current_exception(); + } + + // set the update timestamp + resource.updated = resource_updated; + }); + + if (result) { - modifier(resource); + auto& modified = *found; + + insert_notification_events(resources, modified.version, modified.downgrade_version, modified.type, pre, modified.data, notification_event); } - catch (...) + + if (modifier_exception) { - modifier_exception = std::current_exception(); + std::rethrow_exception(modifier_exception); } - // set the update timestamp - resource.updated = resource_updated; - }); - - if (result) - { - auto& modified = *found; - - insert_notification_events(resources, modified.version, modified.downgrade_version, modified.type, pre, modified.data, notification_event); + return result; } - if (modifier_exception) - { - std::rethrow_exception(modifier_exception); - } - - return result; - } - - // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id - resources::const_iterator find_control_protocol_resource(resources& resources, type type, const id& resource_id) - { - return find_resource_if(resources, type, [resource_id](const nmos::resource& resource) + // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id + resources::const_iterator find_resource(resources& resources, type type, const id& resource_id) { - auto& touchpoints = resource.data.at(nmos::fields::nc::touchpoints); - if (!touchpoints.is_null() && touchpoints.is_array()) - { - auto& tps = touchpoints.as_array(); - auto found_tp = std::find_if(tps.begin(), tps.end(), [resource_id](const web::json::value& touchpoint) + return find_resource_if(resources, type, [resource_id](const nmos::resource& resource) { - auto& resource = nmos::fields::nc::resource(touchpoint); - return (resource_id == nmos::fields::nc::id(resource).as_string()); + auto& touchpoints = resource.data.at(nmos::fields::nc::touchpoints); + if (!touchpoints.is_null() && touchpoints.is_array()) + { + auto& tps = touchpoints.as_array(); + auto found_tp = std::find_if(tps.begin(), tps.end(), [resource_id](const web::json::value& touchpoint) + { + auto& resource = nmos::fields::nc::resource(touchpoint); + return (resource_id == nmos::fields::nc::id(resource).as_string()); + }); + return (tps.end() != found_tp); + } + return false; }); - return (tps.end() != found_tp); - } - return false; - }); - } + } - // method parameters constraints validation - void method_parameters_contraints_validation(const web::json::value& arguments, const web::json::value& nc_method_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) - { - for (const auto& param : nmos::fields::nc::parameters(nc_method_descriptor)) + // method parameters constraints validation + void method_parameters_contraints_validation(const web::json::value& arguments, const web::json::value& nc_method_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { - const auto& name = nmos::fields::nc::name(param); - const auto& constraints = nmos::fields::nc::constraints(param); - const auto& type_name = param.at(nmos::fields::nc::type_name); - if (arguments.is_null() || !arguments.has_field(name)) + for (const auto& param : nmos::fields::nc::parameters(nc_method_descriptor)) { - // missing argument parameter - throw control_protocol_exception("missing argument parameter " + utility::us2s(name)); + const auto& name = nmos::fields::nc::name(param); + const auto& constraints = nmos::fields::nc::constraints(param); + const auto& type_name = param.at(nmos::fields::nc::type_name); + if (arguments.is_null() || !arguments.has_field(name)) + { + // missing argument parameter + throw control_protocol_exception("missing argument parameter " + utility::us2s(name)); + } + details::method_parameter_constraints_validation(arguments.at(name), constraints, { nmos::nc::details::get_datatype_descriptor(type_name, get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); } - details::method_parameter_constraints_validation(arguments.at(name), constraints, { nmos::details::get_datatype_descriptor(type_name, get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); } - } - resources::const_iterator find_control_protocol_resource_by_role_path(const resources& resources, const web::json::array& role_path_) - { - auto role_path = role_path_; - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); - if (resources.end() != resource) + resources::const_iterator find_resource_by_role_path(const resources& resources, const web::json::array& role_path_) { - const auto role = nmos::fields::nc::role(resource->data); - - if (role_path.size() && role == web::json::front(role_path).as_string()) + auto role_path = role_path_; + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); + if (resources.end() != resource) { - role_path.erase(0); + const auto role = nmos::fields::nc::role(resource->data); - if (role_path.size()) + if (role_path.size() && role == web::json::front(role_path).as_string()) { - const auto& block_member_descriptor = details::get_nc_block_member_descriptor(resources, *resource, role_path); - if (!block_member_descriptor.is_null()) + role_path.erase(0); + + if (role_path.size()) { - const auto& oid = nmos::fields::nc::oid(block_member_descriptor); - const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found) + const auto& block_member_descriptor = details::get_nc_block_member_descriptor(resources, *resource, role_path); + if (!block_member_descriptor.is_null()) { - return found; + const auto& oid = nmos::fields::nc::oid(block_member_descriptor); + const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) + { + return found; + } } } - } - else - { - return resource; + else + { + return resource; + } } } + return resources.end(); } - return resources.end(); - } - web::json::value parse_role_path(const utility::string_t& role_path_) - { - // tokenize the role_path with the '.' delimiter - std::list role_path_segments; - boost::algorithm::split(role_path_segments, role_path_, [](utility::char_t c) { return '.' == c; }); + resources::const_iterator find_resource_by_role_path(const resources& resources, const utility::string_t& role_path_) + { + const auto& role_path = details::parse_role_path(role_path_); - return web::json::value_from_elements(role_path_segments); - } + return find_resource_by_role_path(resources, role_path.as_array()); + } + resources::const_iterator find_touchpoint_resource(const resources& resources, const resource& resource) + { + if (!resource.has_data()) + { + return resources.end(); + } - resources::const_iterator find_control_protocol_resource_by_role_path(const resources& resources, const utility::string_t& role_path_) - { - const auto& role_path = parse_role_path(role_path_).as_array(); + const auto& touchpoints = resource.data.at(nmos::fields::nc::touchpoints); - return find_control_protocol_resource_by_role_path(resources, role_path); + if (touchpoints.size() == 0) + { + return resources.end(); + } + + // Hmmmmm we're only getting the first touchpoint resource. There could be more than one. + const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); + return nmos::find_resource(resources, touchpoint_uuid.as_string()); + } } } diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index d15605178..9984c349c 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -13,81 +13,86 @@ namespace nmos control_protocol_exception(const std::string& message) : std::runtime_error(message) {} }; - namespace details + namespace nc { - // get the runtime property constraints of a given property_id - web::json::value get_runtime_property_constraints(const nc_property_id& property_id, const web::json::value& runtime_property_constraints_list); + namespace details + { + // get the runtime property constraints of a given property_id + web::json::value get_runtime_property_constraints(const nc_property_id& property_id, const web::json::value& runtime_property_constraints_list); - // get the datatype descriptor of a specific type_name - web::json::value get_datatype_descriptor(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype); + // get the datatype descriptor of a specific type_name + web::json::value get_datatype_descriptor(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype); - // get the datatype property constraints of a given type_name - web::json::value get_datatype_constraints(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype); + // get the datatype property constraints of a given type_name + web::json::value get_datatype_constraints(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype); - struct datatype_constraints_validation_parameters - { - web::json::value datatype_descriptor; - get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor; - }; - // multiple levels of constraints validation, may throw nmos::control_protocol_exception - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - void constraints_validation(const web::json::value& value, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params); - - // method parameter constraints validation, may throw nmos::control_protocol_exception - void method_parameter_constraints_validation(const web::json::value& data, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params); - } + struct datatype_constraints_validation_parameters + { + web::json::value datatype_descriptor; + get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor; + }; + // multiple levels of constraints validation, may throw nmos::control_protocol_exception + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + void constraints_validation(const web::json::value& value, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params); - // is the given class_id a NcBlock - bool is_nc_block(const nc_class_id& class_id); + // method parameter constraints validation, may throw nmos::control_protocol_exception + void method_parameter_constraints_validation(const web::json::value& data, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params); - // is the given class_id a NcWorker - bool is_nc_worker(const nc_class_id& class_id); + // convert . delimited string into role path object + web::json::value parse_role_path(const utility::string_t& role_path); + } - // is the given class_id a NcManager - bool is_nc_manager(const nc_class_id& class_id); + // is the given class_id a NcBlock + bool is_block(const nc_class_id& class_id); - // is the given class_id a NcDeviceManager - bool is_nc_device_manager(const nc_class_id& class_id); + // is the given class_id a NcWorker + bool is_worker(const nc_class_id& class_id); - // is the given class_id a NcClassManager - bool is_nc_class_manager(const nc_class_id& class_id); + // is the given class_id a NcManager + bool is_manager(const nc_class_id& class_id); - // construct NcClassId - nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const std::vector& suffix); - nc_class_id make_nc_class_id(const nc_class_id& prefix, const std::vector& suffix); // using default authority_key 0 + // is the given class_id a NcDeviceManager + bool is_device_manager(const nc_class_id& class_id); - // find control class property descriptor (NcPropertyDescriptor) - web::json::value find_property_descriptor(const nc_property_id& property_id, const nc_class_id& class_id, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor); + // is the given class_id a NcClassManager + bool is_class_manager(const nc_class_id& class_id); - // get block memeber descriptors - void get_member_descriptors(const resources& resources, const resource& resource, bool recurse, web::json::array& descriptors); + // construct NcClassId + nc_class_id make_class_id(const nc_class_id& prefix, int32_t authority_key, const std::vector& suffix); + nc_class_id make_class_id(const nc_class_id& prefix, const std::vector& suffix); // using default authority_key 0 - // find members with given role name or fragment - void find_members_by_role(const resources& resources, const resource& resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& nc_block_member_descriptors); + // find control class property descriptor (NcPropertyDescriptor) + web::json::value find_property_descriptor(const nc_property_id& property_id, const nc_class_id& class_id, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor); - // find members with given class id - void find_members_by_class_id(const resources& resources, const resource& resource, const nc_class_id& class_id, bool include_derived, bool recurse, web::json::array& descriptors); + // get block memeber descriptors + void get_member_descriptors(const resources& resources, const resource& resource, bool recurse, web::json::array& descriptors); - // push control protocol resource into other control protocol NcBlock resource - void push_back(control_protocol_resource& nc_block_resource, const control_protocol_resource& resource); + // find members with given role name or fragment + void find_members_by_role(const resources& resources, const resource& resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& nc_block_member_descriptors); - // modify a control protocol resource, and insert notification event to all subscriptions - bool modify_control_protocol_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event); + // find members with given class id + void find_members_by_class_id(const resources& resources, const resource& resource, const nc_class_id& class_id, bool include_derived, bool recurse, web::json::array& descriptors); - // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id - resources::const_iterator find_control_protocol_resource(resources& resources, type type, const id& id); + // push control protocol resource into other control protocol NcBlock resource + void push_back(control_protocol_resource& nc_block_resource, const control_protocol_resource& resource); - // find resource based on role path. - resources::const_iterator find_control_protocol_resource_by_role_path(const resources& resources, const web::json::array& role_path); + // modify a control protocol resource, and insert notification event to all subscriptions + bool modify_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event); - // find resource based on role path. Roles in role path string must be delimited with a '.' - resources::const_iterator find_control_protocol_resource_by_role_path(const resources& resources, const utility::string_t& role_path); + // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id + resources::const_iterator find_resource(resources& resources, type type, const id& id); - // convert . delimited string into role path object - web::json::value parse_role_path(const utility::string_t& role_path); + // find resource based on role path. + resources::const_iterator find_resource_by_role_path(const resources& resources, const web::json::array& role_path); - // method parameters constraints validation, may throw nmos::control_protocol_exception - void method_parameters_contraints_validation(const web::json::value& arguments, const web::json::value& nc_method_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor); + // find resource based on role path. Roles in role path string must be delimited with a '.' + resources::const_iterator find_resource_by_role_path(const resources& resources, const utility::string_t& role_path); + + // method parameters constraints validation, may throw nmos::control_protocol_exception + void method_parameters_contraints_validation(const web::json::value& arguments, const web::json::value& nc_method_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor); + + resources::const_iterator find_touchpoint_resource(const resources& resources, const resource& resource); + } } #endif diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index cf1f35a0e..71257a087 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -268,7 +268,7 @@ namespace nmos try { // do method arguments constraints validation - method_parameters_contraints_validation(arguments, nc_method_descriptor, get_control_protocol_datatype_descriptor); + nc::method_parameters_contraints_validation(arguments, nc_method_descriptor, get_control_protocol_datatype_descriptor); // execute the relevant control method handler, then accumulating up their response to reponses // wrap the NcMethodResuls here diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 8f8b6f87b..62b194740 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -63,11 +63,11 @@ BST_TEST_CASE(testIsBlockModified) // root, receivers, mon2 auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); auto monitor_2_oid = oid; - nmos::push_back(receivers, monitor1); + nmos::nc::push_back(receivers, monitor1); // add example-control to root-block - nmos::push_back(receivers, monitor2); + nmos::nc::push_back(receivers, monitor2); // add stereo-gain to root-block - nmos::push_back(root_block, receivers); + nmos::nc::push_back(root_block, receivers); // Create Object Properties Holder auto role_path = value::array(); @@ -161,7 +161,7 @@ BST_TEST_CASE(testIsBlockModified) auto property_value_holders = value::array(); auto members = value::array(); - const auto class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 1 }); + const auto class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 1 }); { const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); push_back(members, block_member_descriptor); @@ -183,7 +183,7 @@ BST_TEST_CASE(testIsBlockModified) auto property_value_holders = value::array(); auto members = value::array(); - const auto class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 1 }); + const auto class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 1 }); { const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), 20); push_back(members, block_member_descriptor); @@ -205,7 +205,7 @@ BST_TEST_CASE(testIsBlockModified) auto property_value_holders = value::array(); auto members = value::array(); - const auto class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, 0, { 1 }); + const auto class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 1 }); { const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, false, class_id, U("monitor 1"), receiver_block_oid); push_back(members, block_member_descriptor); @@ -247,13 +247,13 @@ BST_TEST_CASE(testGetRolePath) nmos::nc_class_id monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); - nmos::push_back(receivers, monitor1); + nmos::nc::push_back(receivers, monitor1); // add example-control to root-block - nmos::push_back(receivers, monitor2); + nmos::nc::push_back(receivers, monitor2); // add stereo-gain to root-block - nmos::push_back(root_block, receivers); + nmos::nc::push_back(root_block, receivers); // add class-manager to root-block - nmos::push_back(root_block, class_manager); + nmos::nc::push_back(root_block, class_manager); insert_resource(resources, std::move(root_block)); insert_resource(resources, std::move(class_manager)); insert_resource(resources, std::move(receivers)); @@ -269,7 +269,7 @@ BST_TEST_CASE(testGetRolePath) for (const auto& expected_role_path : expected_role_paths.as_array()) { - const auto& resource = find_control_protocol_resource_by_role_path(resources, expected_role_path.as_array()); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, expected_role_path.as_array()); const auto actual_role_path = nmos::get_role_path(resources, *resource); BST_CHECK_EQUAL(expected_role_path.as_array(), actual_role_path); } @@ -304,13 +304,13 @@ BST_TEST_CASE(testApplyBackupDataSet) auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); - nmos::push_back(receivers, monitor1); + nmos::nc::push_back(receivers, monitor1); // add example-control to root-block - nmos::push_back(receivers, monitor2); + nmos::nc::push_back(receivers, monitor2); // add stereo-gain to root-block - nmos::push_back(root_block, receivers); + nmos::nc::push_back(root_block, receivers); // add class-manager to root-block - nmos::push_back(root_block, class_manager); + nmos::nc::push_back(root_block, class_manager); insert_resource(resources, std::move(root_block)); insert_resource(resources, std::move(class_manager)); insert_resource(resources, std::move(receivers)); @@ -357,7 +357,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one @@ -394,7 +394,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; bool validate = true; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one @@ -432,7 +432,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; bool validate = true; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one @@ -482,7 +482,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; bool validate = true; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one @@ -527,7 +527,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one @@ -561,7 +561,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one @@ -596,7 +596,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; bool validate = true; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one @@ -640,7 +640,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; bool validate = true; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one @@ -684,7 +684,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; bool validate = true; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one @@ -739,13 +739,13 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); - nmos::push_back(receivers, monitor1); + nmos::nc::push_back(receivers, monitor1); // add example-control to root-block - nmos::push_back(receivers, monitor2); + nmos::nc::push_back(receivers, monitor2); // add stereo-gain to root-block - nmos::push_back(root_block, receivers); + nmos::nc::push_back(root_block, receivers); // add class-manager to root-block - nmos::push_back(root_block, class_manager); + nmos::nc::push_back(root_block, class_manager); insert_resource(resources, std::move(root_block)); insert_resource(resources, std::move(class_manager)); insert_resource(resources, std::move(receivers)); @@ -772,7 +772,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) bool validate = true; const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one @@ -799,7 +799,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) bool validate = true; const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one @@ -829,7 +829,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; bool validate = true; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one @@ -857,7 +857,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) bool validate = true; const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; - const auto& resource = find_control_protocol_resource_by_role_path(resources, target_role_path.as_array()); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // expectation is there will be a result for each of the object_properties_holders i.e. one diff --git a/Development/nmos/test/control_protocol_test.cpp b/Development/nmos/test/control_protocol_test.cpp index 692eba769..bf80ad6ea 100644 --- a/Development/nmos/test/control_protocol_test.cpp +++ b/Development/nmos/test/control_protocol_test.cpp @@ -630,42 +630,42 @@ BST_TEST_CASE(testNcDatatypeDescriptorPrimitive) BST_TEST_CASE(testNcClassId) { - BST_REQUIRE_EQUAL(false, nmos::is_nc_block({ })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_block({ 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_block({ 1, 2 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_block({ 1, 2, 0 })); - BST_REQUIRE(nmos::is_nc_block(nmos::nc_block_class_id)); - BST_REQUIRE(nmos::is_nc_block(nmos::make_nc_class_id(nmos::nc_block_class_id, { 1 }))); - - BST_REQUIRE_EQUAL(false, nmos::is_nc_worker({ })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_worker({ 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_worker({ 1, 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_worker({ 1, 1, 1 })); - BST_REQUIRE(nmos::is_nc_worker(nmos::nc_worker_class_id)); - BST_REQUIRE(nmos::is_nc_worker(nmos::make_nc_class_id(nmos::nc_worker_class_id, { 1 }))); - - BST_REQUIRE_EQUAL(false, nmos::is_nc_manager({ })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_manager({ 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_manager({ 1, 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_manager({ 1, 1, 1 })); - BST_REQUIRE(nmos::is_nc_manager(nmos::nc_manager_class_id)); - BST_REQUIRE(nmos::is_nc_manager(nmos::make_nc_class_id(nmos::nc_manager_class_id, { 1 }))); - - BST_REQUIRE_EQUAL(false, nmos::is_nc_device_manager({ })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_device_manager({ 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_device_manager({ 1, 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_device_manager({ 1, 1, 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_device_manager({ 1, 3, 2 })); - BST_REQUIRE(nmos::is_nc_device_manager(nmos::nc_device_manager_class_id)); - BST_REQUIRE(nmos::is_nc_device_manager(nmos::make_nc_class_id(nmos::nc_device_manager_class_id, { 1 }))); - - BST_REQUIRE_EQUAL(false, nmos::is_nc_class_manager({ })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_class_manager({ 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_class_manager({ 1, 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_class_manager({ 1, 1, 1 })); - BST_REQUIRE_EQUAL(false, nmos::is_nc_class_manager({ 1, 3, 1 })); - BST_REQUIRE(nmos::is_nc_class_manager(nmos::nc_class_manager_class_id)); - BST_REQUIRE(nmos::is_nc_class_manager(nmos::make_nc_class_id(nmos::nc_class_manager_class_id, { 1 }))); + BST_REQUIRE_EQUAL(false, nmos::nc::is_block({ })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_block({ 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_block({ 1, 2 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_block({ 1, 2, 0 })); + BST_REQUIRE(nmos::nc::is_block(nmos::nc_block_class_id)); + BST_REQUIRE(nmos::nc::is_block(nmos::nc::make_class_id(nmos::nc_block_class_id, { 1 }))); + + BST_REQUIRE_EQUAL(false, nmos::nc::is_worker({ })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_worker({ 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_worker({ 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_worker({ 1, 1, 1 })); + BST_REQUIRE(nmos::nc::is_worker(nmos::nc_worker_class_id)); + BST_REQUIRE(nmos::nc::is_worker(nmos::nc::make_class_id(nmos::nc_worker_class_id, { 1 }))); + + BST_REQUIRE_EQUAL(false, nmos::nc::is_manager({ })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_manager({ 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_manager({ 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_manager({ 1, 1, 1 })); + BST_REQUIRE(nmos::nc::is_manager(nmos::nc_manager_class_id)); + BST_REQUIRE(nmos::nc::is_manager(nmos::nc::make_class_id(nmos::nc_manager_class_id, { 1 }))); + + BST_REQUIRE_EQUAL(false, nmos::nc::is_device_manager({ })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_device_manager({ 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_device_manager({ 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_device_manager({ 1, 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_device_manager({ 1, 3, 2 })); + BST_REQUIRE(nmos::nc::is_device_manager(nmos::nc_device_manager_class_id)); + BST_REQUIRE(nmos::nc::is_device_manager(nmos::nc::make_class_id(nmos::nc_device_manager_class_id, { 1 }))); + + BST_REQUIRE_EQUAL(false, nmos::nc::is_class_manager({ })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_class_manager({ 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_class_manager({ 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_class_manager({ 1, 1, 1 })); + BST_REQUIRE_EQUAL(false, nmos::nc::is_class_manager({ 1, 3, 1 })); + BST_REQUIRE(nmos::nc::is_class_manager(nmos::nc_class_manager_class_id)); + BST_REQUIRE(nmos::nc::is_class_manager(nmos::nc::make_class_id(nmos::nc_class_manager_class_id, { 1 }))); } BST_TEST_CASE(testFindProperty) @@ -681,22 +681,22 @@ BST_TEST_CASE(testFindProperty) { // valid - find members property in NcBlock - auto property = nmos::find_property_descriptor(nc_block_members_property_id, nc_block_class_id, get_control_protocol_class_descriptor); + auto property = nmos::nc::find_property_descriptor(nc_block_members_property_id, nc_block_class_id, get_control_protocol_class_descriptor); BST_REQUIRE(!property.is_null()); } { // invalid - find members property in NcWorker - auto property = nmos::find_property_descriptor(nc_block_members_property_id, nc_worker_class_id, get_control_protocol_class_descriptor); + auto property = nmos::nc::find_property_descriptor(nc_block_members_property_id, nc_worker_class_id, get_control_protocol_class_descriptor); BST_REQUIRE(property.is_null()); } { // invalid - find unknown propertry in NcBlock - auto property = nmos::find_property_descriptor(invalid_property_id, nc_block_class_id, get_control_protocol_class_descriptor); + auto property = nmos::nc::find_property_descriptor(invalid_property_id, nc_block_class_id, get_control_protocol_class_descriptor); BST_REQUIRE(property.is_null()); } { // invalid - find unknown property in unknown class - auto property = nmos::find_property_descriptor(invalid_property_id, invalid_class_id, get_control_protocol_class_descriptor); + auto property = nmos::nc::find_property_descriptor(invalid_property_id, invalid_class_id, get_control_protocol_class_descriptor); BST_REQUIRE(property.is_null()); } } @@ -799,130 +799,130 @@ BST_TEST_CASE(testConstraints) control_protocol_state.insert(nmos::experimental::datatype_descriptor{ no_constraints_string_seq_datatype }); // test get_runtime_property_constraints - BST_REQUIRE_EQUAL(nmos::details::get_runtime_property_constraints(property_string_id, runtime_property_constraints), runtime_property_string_constraints); - BST_REQUIRE_EQUAL(nmos::details::get_runtime_property_constraints(property_int32_id, runtime_property_constraints), runtime_property_int32_constraints); - BST_REQUIRE_EQUAL(nmos::details::get_runtime_property_constraints(unknown_property_id, runtime_property_constraints), value::null()); + BST_REQUIRE_EQUAL(nmos::nc::details::get_runtime_property_constraints(property_string_id, runtime_property_constraints), runtime_property_string_constraints); + BST_REQUIRE_EQUAL(nmos::nc::details::get_runtime_property_constraints(property_int32_id, runtime_property_constraints), runtime_property_int32_constraints); + BST_REQUIRE_EQUAL(nmos::nc::details::get_runtime_property_constraints(unknown_property_id, runtime_property_constraints), value::null()); // string property constraints validation // runtime property constraints validation - const nmos::details::datatype_constraints_validation_parameters with_constraints_string_constraints_validation_params{ with_constraints_string_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value::string(U("1234567890")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("12345678901")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("123456789A")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("1234567890")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("12345678901")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890")), 1 }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + const nmos::nc::details::datatype_constraints_validation_parameters with_constraints_string_constraints_validation_params{ with_constraints_string_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value::string(U("1234567890")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value::string(U("12345678901")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value::string(U("123456789A")), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("1234567890")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("1234567890")), value::string(U("12345678901")) }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("1234567890")), 1 }), runtime_property_string_constraints, property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); // property constraints validation - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value::string(U("abcde")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("abcdef")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("abcd1")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcde")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcdef")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("abcde")), 1 }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value::string(U("abcde")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value::string(U("abcdef")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value::string(U("abcd1")), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcde")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("abcde")), value::string(U("abcdef")) }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("abcde")), 1 }), value::null(), property_string_constraints, with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); // datatype constraints validation - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value::string(U("1a")), value::null(), value::null(), with_constraints_string_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("1a2")), value::null(), value::null(), with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("1*")), value::null(), value::null(), with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); - const nmos::details::datatype_constraints_validation_parameters no_constraints_string_constraints_validation_params{ no_constraints_string_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value::string(U("1234567890-abcde-!\"$%^&*()_+=")), value::null(), value::null(), no_constraints_string_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value::string(U("1a")), value::null(), value::null(), with_constraints_string_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value::string(U("1a2")), value::null(), value::null(), with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value::string(U("1*")), value::null(), value::null(), with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_string_constraints_validation_params{ no_constraints_string_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value::string(U("1234567890-abcde-!\"$%^&*()_+=")), value::null(), value::null(), no_constraints_string_constraints_validation_params)); // number property constraints validation // runtime property constraints validation - const nmos::details::datatype_constraints_validation_parameters with_constraints_int32_constraints_validation_params{ with_constraints_int32_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(10, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(1000, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(9, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(1001, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ 10, 1000 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 10, 1001 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 10, value::string(U("a")) }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + const nmos::nc::details::datatype_constraints_validation_parameters with_constraints_int32_constraints_validation_params{ with_constraints_int32_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(10, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(1000, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(9, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(1001, runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value_of({ 10, 1000 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 10, 1001 }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 10, value::string(U("a")) }), runtime_property_int32_constraints, property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); // property constraints validation - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(50, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(500, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(45, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(505, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(499, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ 50, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 49, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 50, 501 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 45, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 50, value::string(U("a")) }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(50, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(500, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(45, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(505, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(499, value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value_of({ 50, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 49, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 50, 501 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 45, 500 }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 50, value::string(U("a")) }), value::null(), property_int32_constraints, with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); // datatype constraints validation - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(100, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(250, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(90, value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(260, value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(99, value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(100, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(250, value::null(), value::null(), with_constraints_int32_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(90, value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(260, value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(99, value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); // int16 datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters no_constraints_int16_constraints_validation_params{ no_constraints_int16_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_int16_constraints_validation_params{ no_constraints_int16_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int16_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int16_constraints_validation_params)); // int32 datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters no_constraints_int32_constraints_validation_params{ no_constraints_int32_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_int32_constraints_validation_params{ no_constraints_int32_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(int64_t(std::numeric_limits::min()) - 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(int64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int32_constraints_validation_params)); // int64 datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters no_constraints_int64_constraints_validation_params{ no_constraints_int64_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_int64_constraints_validation_params{ no_constraints_int64_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params)); // uint16 datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters no_constraints_uint16_constraints_validation_params{ no_constraints_uint16_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_uint16_constraints_validation_params{ no_constraints_uint16_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint16_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint16_constraints_validation_params)); // uint32 datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters no_constraints_uint32_constraints_validation_params{ no_constraints_uint32_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_uint32_constraints_validation_params{ no_constraints_uint32_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(uint64_t(std::numeric_limits::max()) + 1, value::null(), value::null(), no_constraints_uint32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint32_constraints_validation_params)); // uint64 datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters no_constraints_uint64_constraints_validation_params{ no_constraints_uint64_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint64_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_uint64_constraints_validation_params{ no_constraints_uint64_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(-1, value::null(), value::null(), no_constraints_uint64_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_int64_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_uint64_constraints_validation_params)); // float32 datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters no_constraints_float32_constraints_validation_params{ no_constraints_float32_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(std::numeric_limits::lowest(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::lowest(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(0.0, value::null(), value::null(), no_constraints_float32_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(-1000.0, value::null(), value::null(), no_constraints_float32_constraints_validation_params)); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_float32_constraints_validation_params{ no_constraints_float32_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::lowest(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::lowest(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(0.0, value::null(), value::null(), no_constraints_float32_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(-1000.0, value::null(), value::null(), no_constraints_float32_constraints_validation_params)); // float64 datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters no_constraints_float64_constraints_validation_params{ no_constraints_float64_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(1000, value::null(), value::null(), no_constraints_float64_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(1000.0, value::null(), value::null(), no_constraints_float64_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_float64_constraints_validation_params{ no_constraints_float64_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(1000, value::null(), value::null(), no_constraints_float64_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(1000.0, value::null(), value::null(), no_constraints_float64_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::min(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(std::numeric_limits::max(), value::null(), value::null(), no_constraints_float64_constraints_validation_params)); // enum property datatype constraints validation - const nmos::details::datatype_constraints_validation_parameters enum_constraints_validation_params{ enum_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(enum_value::foo, value::null(), value::null(), enum_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(4, value::null(), value::null(), enum_constraints_validation_params), nmos::control_protocol_exception); + const nmos::nc::details::datatype_constraints_validation_parameters enum_constraints_validation_params{ enum_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(enum_value::foo, value::null(), value::null(), enum_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(4, value::null(), value::null(), enum_constraints_validation_params), nmos::control_protocol_exception); // invalid data vs primitive datatype constraints - const nmos::details::datatype_constraints_validation_parameters no_constraints_string_seq_constraints_validation_params{ no_constraints_string_seq_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"$%^&*()_+=")), value::string(U("1234567890-abcde-!\"$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(1, value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), nmos::control_protocol_exception); - const nmos::details::datatype_constraints_validation_parameters no_constraints_int32_seq_constraints_validation_params{ no_constraints_int32_seq_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value::string(U("1234567890-abcde-!\"$%^&*()_+=")), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_string_seq_constraints_validation_params{ no_constraints_string_seq_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"$%^&*()_+=")), value::string(U("1234567890-abcde-!\"$%^&*()_+=")) }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(1, value::null(), value::null(), no_constraints_string_seq_constraints_validation_params), nmos::control_protocol_exception); + const nmos::nc::details::datatype_constraints_validation_parameters no_constraints_int32_seq_constraints_validation_params{ no_constraints_int32_seq_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value_of({ 1 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ value::string(U("1234567890-abcde-!\"$%^&*()_+=")) }), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value::string(U("1234567890-abcde-!\"$%^&*()_+=")), value::null(), value::null(), no_constraints_int32_seq_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_int32_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(value_of({ 1, 2 }), value::null(), value::null(), with_constraints_string_constraints_validation_params), nmos::control_protocol_exception); // struct property datatype constraints validation const auto good_struct1 = value_of({ @@ -1833,29 +1833,29 @@ BST_TEST_CASE(testConstraints) { U("sequenceStructPropertyNullable"), value::null() } }); - const nmos::details::datatype_constraints_validation_parameters struct_constraints_validation_params{ struct_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(good_struct1, value::null(), value::null(), struct_constraints_validation_params)); - BST_REQUIRE_NO_THROW(nmos::details::constraints_validation(good_struct2, value::null(), value::null(), struct_constraints_validation_params)); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_4, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_5, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_5_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_5_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_5_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_6, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_6_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_6_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_6_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_7, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_7_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_7_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct2_7_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct3_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct3_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); - BST_REQUIRE_THROW(nmos::details::constraints_validation(bad_struct3_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + const nmos::nc::details::datatype_constraints_validation_parameters struct_constraints_validation_params{ struct_datatype, nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state) }; + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(good_struct1, value::null(), value::null(), struct_constraints_validation_params)); + BST_REQUIRE_NO_THROW(nmos::nc::details::constraints_validation(good_struct2, value::null(), value::null(), struct_constraints_validation_params)); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_4, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_5, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_5_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_5_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_5_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_6, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_6_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_6_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_6_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_7, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_7_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_7_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct2_7_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct3_1, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct3_2, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); + BST_REQUIRE_THROW(nmos::nc::details::constraints_validation(bad_struct3_3, value::null(), value::null(), struct_constraints_validation_params), nmos::control_protocol_exception); } diff --git a/Development/nmos/test/control_protocol_utils_test.cpp b/Development/nmos/test/control_protocol_utils_test.cpp new file mode 100644 index 000000000..bccda1ec1 --- /dev/null +++ b/Development/nmos/test/control_protocol_utils_test.cpp @@ -0,0 +1,70 @@ +// The first "test" is of course whether the header compiles standalone +#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_resources.h" +#include "nmos/control_protocol_state.h" +#include "nmos/control_protocol_typedefs.h" +#include "nmos/control_protocol_utils.h" + +#include "nmos/is04_versions.h" + +#include "bst/test/test.h" + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testFindTouchpointResources) +{ + using web::json::value_of; + using web::json::value; + + const auto touchpoint1_id = U("1000"); + auto touchpoint1_data = value_of({ + { nmos::fields::id, touchpoint1_id }, + { nmos::fields::version, nmos::make_version() }, + { nmos::fields::label, U("touchpoint1") }, + { nmos::fields::description, U("touchpoint1") }, + { nmos::fields::tags, value::null() } + }); + nmos::resource touchpoint1 = { nmos::is04_versions::v1_3, nmos::types::node, std::move(touchpoint1_data), false }; + + const auto touchpoint2_id = U("1001"); + auto touchpoint2_data = value_of({ + { nmos::fields::id, touchpoint2_id }, + { nmos::fields::version, nmos::make_version() }, + { nmos::fields::label, U("touchpoint2") }, + { nmos::fields::description, U("touchpoint2") }, + { nmos::fields::tags, value::null() } + }); + nmos::resource touchpoint2 = { nmos::is04_versions::v1_3, nmos::types::node, std::move(touchpoint2_data), false }; + + const auto non_existant_id = U("1002"); + + // Create Device Model + auto oid = nmos::root_block_oid; + auto monitor1 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint1_id})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint2_id})} })); + auto monitor3 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon3"), U("monitor 3"), U("monitor 3"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, non_existant_id})} })); + + nmos::resources resources; + // Insert dummy NMOS resources + insert_resource(resources, std::move(touchpoint1)); + insert_resource(resources, std::move(touchpoint2)); + + { + const auto& touchpoint = nmos::nc::find_touchpoint_resource(resources, monitor1); + + BST_CHECK_EQUAL(touchpoint1_id, nmos::fields::id(touchpoint->data)); + BST_CHECK_EQUAL(U("touchpoint1"), nmos::fields::label(touchpoint->data)); + BST_CHECK_EQUAL(U("touchpoint1"), nmos::fields::description(touchpoint->data)); + } + { + const auto& touchpoint = nmos::nc::find_touchpoint_resource(resources, monitor2); + + BST_CHECK_EQUAL(touchpoint2_id, nmos::fields::id(touchpoint->data)); + BST_CHECK_EQUAL(U("touchpoint2"), nmos::fields::label(touchpoint->data)); + BST_CHECK_EQUAL(U("touchpoint2"), nmos::fields::description(touchpoint->data)); + } + { + const auto& touchpoint = nmos::nc::find_touchpoint_resource(resources, monitor3); + + BST_CHECK_EQUAL(touchpoint, resources.end()); + } +} From 2889974bfed3840d2c92e413e8476655ca74ef65 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 15 Jan 2025 14:43:29 +0000 Subject: [PATCH 164/250] Add get_object_properties_holder and get_child_object_properties_holders helper functions --- .../nmos-cpp-node/node_implementation.cpp | 2 +- Development/nmos/configuration_handlers.h | 2 +- Development/nmos/configuration_utils.cpp | 59 ++++---- Development/nmos/configuration_utils.h | 6 + .../nmos/test/configuration_utils_test.cpp | 126 +++++++++++++++++- 5 files changed, 164 insertions(+), 31 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 684e9288b..5cb755472 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1757,7 +1757,7 @@ nmos::filter_property_value_holders_handler make_filter_property_value_holders_h web::json::push_back(modifiable_property_value_holders, property_value); } } - return modifiable_property_value_holders; + return modifiable_property_value_holders.as_array(); }; } diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h index 19ee9b9d0..7d44337b0 100644 --- a/Development/nmos/configuration_handlers.h +++ b/Development/nmos/configuration_handlers.h @@ -19,7 +19,7 @@ namespace nmos } // This callback is invoked if attempting to modify read only properties when restoring a configuration. // This function should modify the Device Model object directly and return a corresponding NcObjectPropertiesSetValidation object - typedef std::function filter_property_value_holders_handler; + typedef std::function filter_property_value_holders_handler; // This callback is invoked if attempting to modify a rebuildable block when restoring a configuration. // This function should handle the modification of the Device Model and any corresponding NMOS resources diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 5031a01c0..f78e845f6 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -142,33 +142,41 @@ namespace nmos return false; } - web::json::value modify_device_model(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::array get_object_properties_holder(const web::json::array& object_properties_holders, const web::json::array& target_role_path) { - auto object_properties_set_validation_values = web::json::value::array(); - - // Filter for the target_role_path and child objects - // - // hmmmmm, I don't like this two step filter process - creating a boost array and then converting to a json array. - // Could this be done in a single step? - const auto& filtered_object_properties_holders = boost::copy_range>(object_properties_holders + const auto& target_object_properties_holders = boost::copy_range>(object_properties_holders | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) { - return is_role_path_root(target_role_path, nmos::fields::nc::path(object_properties_holder)); + return target_role_path == nmos::fields::nc::path(object_properties_holder); }) ); - web::json::value child_object_properties_holders = web::json::value::array(); - for (const auto& filtered_holder : filtered_object_properties_holders) - { - web::json::push_back(child_object_properties_holders, filtered_holder); - } + return web::json::value_from_elements(target_object_properties_holders).as_array(); + } - // get object_properties_holder for the target role path, if there is one - const auto& target_object_properties_holders = boost::copy_range>(object_properties_holders + web::json::array get_child_object_properties_holders(const web::json::array& object_properties_holders, const web::json::array& target_role_path) + { + const auto& child_object_properties_holders = boost::copy_range>(object_properties_holders | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) { - return target_role_path == nmos::fields::nc::path(object_properties_holder); + return is_role_path_root(target_role_path, nmos::fields::nc::path(object_properties_holder)); }) ); + return web::json::value_from_elements(child_object_properties_holders).as_array(); + } + + web::json::value modify_device_model(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + { + auto object_properties_set_validation_values = web::json::value::array(); + + // Filter for the target_role_path and child objects + // + // hmmmmm, I don't like this two step filter process - creating a boost array and then converting to a json array. + // Could this be done in a single step? + const auto& child_object_properties_holders = get_child_object_properties_holders(object_properties_holders, target_role_path); + + // get object_properties_holder for the target role path, if there is one + const auto& target_object_properties_holders = get_object_properties_holder(object_properties_holders, target_role_path); + // there should be 0 or 1 object_properties_holder for any role path. if (target_object_properties_holders.size() > 1) { @@ -188,7 +196,7 @@ namespace nmos if (modify_rebuildable_block) { // call back to application code which will return an object_properties_set_validation_values object - return modify_rebuildable_block(resource, target_role_path, child_object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor); + return modify_rebuildable_block(resource, target_role_path, child_object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor); } else { @@ -213,7 +221,7 @@ namespace nmos auto child_role_path = web::json::value_from_elements(target_role_path); web::json::push_back(child_role_path, nmos::fields::nc::role(child->data)); - web::json::value child_object_properties_set_validation_values = modify_device_model(resources, *child, child_role_path.as_array(), child_object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, *child, child_role_path.as_array(), child_object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); // Hmmm, there must be a better way of merging two json array objects for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) { @@ -227,7 +235,6 @@ namespace nmos { auto property_restore_notices = web::json::value::array(); // Validate property_values - filter out the incorrect, ignored or unallowed values - // Hmm as above, don't like the two step filter process here const auto& filtered_property_values = boost::copy_range>(nmos::fields::nc::values(target_object_properties_holder) | boost::adaptors::filtered([&property_restore_notices, &resource, class_id, get_control_protocol_class_descriptor, restore_mode](const web::json::value& property_value) { @@ -238,20 +245,16 @@ namespace nmos && details::is_property_value_valid(property_restore_notices, property_value, property_descriptor, restore_mode, bool(nmos::fields::nc::is_rebuildable(resource.data))); }) ); - auto property_modify_list = web::json::value::array(); - for (const auto& property_value : filtered_property_values) - { - web::json::push_back(property_modify_list, property_value); - } + auto property_modify_list = web::json::value_from_elements(filtered_property_values).as_array(); - if (details::is_contains_read_only_property(property_modify_list.as_array(), class_id, get_control_protocol_class_descriptor)) + if (details::is_contains_read_only_property(property_modify_list, class_id, get_control_protocol_class_descriptor)) { if (filter_property_value_holders) { // If the property_modify_list contains read only properties then we call back to the application code to // check that it's OK to change those value. Bear in mind that they could be the class Id, or the oid or some other // property that we don't want changed - property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list.as_array(), recurse, restore_mode, validate, property_restore_notices.as_array(), get_control_protocol_class_descriptor); + property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list, recurse, restore_mode, validate, property_restore_notices.as_array(), get_control_protocol_class_descriptor); } else { @@ -261,7 +264,7 @@ namespace nmos continue; } } - for (const auto& property_value : property_modify_list.as_array()) + for (const auto& property_value : property_modify_list) { const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); diff --git a/Development/nmos/configuration_utils.h b/Development/nmos/configuration_utils.h index f0b552b0c..5bf6fa8e6 100644 --- a/Development/nmos/configuration_utils.h +++ b/Development/nmos/configuration_utils.h @@ -17,6 +17,12 @@ namespace nmos // Get role path of resource given the Device Model resources web::json::array get_role_path(const nmos::resources& resources, const nmos::resource& resource); + // Get object_properties_holder for specified target_role_path + web::json::array get_object_properties_holder(const web::json::array& object_properties_holders, const web::json::array& target_role_path); + + // Get object_properties_holder for specified target_role_path and all its child object_properties_holders + web::json::array get_child_object_properties_holders(const web::json::array& object_properties_holders, const web::json::array& target_role_path); + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); web::json::value get_property_value_holder(const web::json::value& object_properties_holder, const nmos::nc_property_id& property_id); diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 62b194740..f17627ebd 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -223,6 +223,130 @@ BST_TEST_CASE(testIsBlockModified) } } +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testGetObjectPropertiesHolder) +{ + using web::json::value_of; + using web::json::value; + + // Create Object Properties Holder + auto object_properties_holders = value::array(); + + { + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + } + { + const auto role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + } + { + const auto role_path = value_of({ U("root"), U("senders"), U("mon1") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + } + + { + const auto target_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + web::json::array object_property_holder = nmos::get_object_properties_holder(object_properties_holders.as_array(), target_role_path.as_array()); + BST_REQUIRE_EQUAL(1, object_property_holder.size()); + BST_CHECK_EQUAL(target_role_path.as_array(), nmos::fields::nc::path(*object_property_holder.begin())); + } + { + const auto target_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + web::json::array object_property_holder = nmos::get_object_properties_holder(object_properties_holders.as_array(), target_role_path.as_array()); + BST_REQUIRE_EQUAL(1, object_property_holder.size()); + BST_CHECK_EQUAL(target_role_path.as_array(), nmos::fields::nc::path(*object_property_holder.begin())); + } + { + const auto target_role_path = value_of({ U("root"), U("receivers") }); + web::json::array object_property_holder = nmos::get_object_properties_holder(object_properties_holders.as_array(), target_role_path.as_array()); + BST_REQUIRE_EQUAL(0, object_property_holder.size()); + } + { + const auto target_role_path = value_of({ U("root"), U("does_not_exist") }); + web::json::array object_property_holder = nmos::get_object_properties_holder(object_properties_holders.as_array(), target_role_path.as_array()); + BST_REQUIRE_EQUAL(0, object_property_holder.size()); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testGetChildObjectPropertiesHolders) +{ + using web::json::value_of; + using web::json::value; + + // Create Object Properties Holder + auto object_properties_holders = value::array(); + + { + const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + } + { + const auto role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + } + { + const auto role_path = value_of({ U("root"), U("senders"), U("mon1") }); + auto property_value_holders = value::array(); + const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + push_back(property_value_holders, property_value_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + push_back(object_properties_holders, object_properties_holder); + } + + { + const auto target_role_path = value_of({ U("root"), U("receivers") }); + + const auto child_object_properties_holders = nmos::get_child_object_properties_holders(object_properties_holders.as_array(), target_role_path.as_array()); + + BST_REQUIRE_EQUAL(2, child_object_properties_holders.size()); + + const auto& object_properties_holder1 = nmos::get_object_properties_holder(child_object_properties_holders, value_of({ U("root"), U("receivers"), U("mon1") }).as_array()); + BST_CHECK_EQUAL(1, object_properties_holder1.size()); + + const auto& object_properties_holder2 = nmos::get_object_properties_holder(child_object_properties_holders, value_of({ U("root"), U("receivers"), U("mon2") }).as_array()); + BST_CHECK_EQUAL(1, object_properties_holder2.size()); + } + { + const auto target_role_path = value_of({ U("root"), U("receivers"), U("mon1")}); + + const auto child_object_properties_holders = nmos::get_child_object_properties_holders(object_properties_holders.as_array(), target_role_path.as_array()); + + BST_REQUIRE_EQUAL(1, child_object_properties_holders.size()); + + const auto& object_properties_holder1 = nmos::get_object_properties_holder(child_object_properties_holders, value_of({ U("root"), U("receivers"), U("mon1") }).as_array()); + BST_CHECK_EQUAL(1, object_properties_holder1.size()); + } + { + const auto target_role_path = value_of({ U("root"), U("does_not_exist") }); + + const auto child_object_properties_holders = nmos::get_child_object_properties_holders(object_properties_holders.as_array(), target_role_path.as_array()); + + BST_REQUIRE_EQUAL(0, child_object_properties_holders.size()); + } +} + //////////////////////////////////////////////////////////////////////////////////////////// BST_TEST_CASE(testGetRolePath) { @@ -330,7 +454,7 @@ BST_TEST_CASE(testApplyBackupDataSet) { web::json::push_back(modifiable_property_value_holders, property_value); } - return modifiable_property_value_holders; + return modifiable_property_value_holders.as_array(); }; nmos::modify_rebuildable_block_handler modify_rebuildable_block = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { From a1ff8a91384d9919199fa3384165a807d440b006 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 15 Jan 2025 15:12:00 +0000 Subject: [PATCH 165/250] Simplify code --- .../nmos-cpp-node/node_implementation.cpp | 31 ++++--------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 5cb755472..593ab9147 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1773,12 +1773,7 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo // Validate the object_properties_holder // Find object_properties_holder for resource - const auto& filtered_holders = boost::copy_range>(object_properties_holders - | boost::adaptors::filtered([&resources, &resource](const web::json::value& object_properties_holder) - { - return nmos::fields::nc::path(object_properties_holder) == nmos::get_role_path(resources, resource); - }) - ); + const auto& filtered_holders = nmos::get_object_properties_holder(object_properties_holders, nmos::get_role_path(resources, resource)); if (filtered_holders.size() != 1) { @@ -1786,30 +1781,21 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo // Error return web::json::value::array(); } - - const auto& object_properties_holder = *filtered_holders.begin(); - const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); - if (!nmos::nc::is_block(class_id)) + if (!nmos::nc::is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)))) { // Error return web::json::value::array(); } - const auto& block_members_properties_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) - | boost::adaptors::filtered([](const web::json::value& property_value_holder) - { - return nmos::nc_property_id(2, 2) == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value_holder)); - }) - ); - // There should only be a single property holder for the members - if (block_members_properties_holders.size() != 1) + const auto& block_members_properties_holder = nmos::get_property_value_holder(*filtered_holders.begin(), nmos::nc_property_id(2, 2)); + + if (block_members_properties_holder == web::json::value::null()) { // Error return web::json::value::array(); } - const auto& members_property_holder = *block_members_properties_holders.begin(); - const auto& restore_members = nmos::fields::nc::value(members_property_holder); + const auto& restore_members = nmos::fields::nc::value(block_members_properties_holder); const auto& reference_members = nmos::fields::nc::members(resource.data); std::vector members_to_remove; @@ -2001,11 +1987,6 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo // insert resources insert_resource(model.node_resources, std::move(receiver)); insert_resource(resources, std::move(receiver_monitor)); - - // Hmmmmmmmmmmmmmmmmmmmmmmmmmmmmm - // create some helper functions to do things like: - // - manipulate the object_properties_holder to create a json resource based on the object_properties_holders so don't have to keep querying json - // - functions to compare device model to object_properties_holder to show differences } } From ff7027e48c23666d1bea4f3506cec20fdb7b508c Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 27 Jan 2025 14:13:24 +0000 Subject: [PATCH 166/250] Remove redundant restore_mode parameter. Fix locking issue --- .../nmos-cpp-node/node_implementation.cpp | 11 ++-- Development/nmos/configuration_api.cpp | 55 ++++++++++--------- Development/nmos/configuration_handlers.h | 4 +- Development/nmos/configuration_utils.cpp | 6 +- .../nmos/test/configuration_utils_test.cpp | 6 +- 5 files changed, 41 insertions(+), 41 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 593ab9147..0b8d207b5 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1731,7 +1731,7 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control // Example Device Configuration callback for validating a back-up dataset nmos::filter_property_value_holders_handler make_filter_property_value_holders_handler(nmos::resources& resources, slog::base_gate& gate) { - return [&resources, &gate](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, const web::json::value& restore_mode, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + return [&resources, &gate](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { // Use this function to filter which of the properties in the object should be modified by the configuration API slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do filter_property_value_holders"; @@ -1764,7 +1764,7 @@ nmos::filter_property_value_holders_handler make_filter_property_value_holders_h // Example Device Configuration callback for restoring a back-up dataset nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmos::node_model& model, slog::base_gate& gate) { - return [&model, &gate](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + return [&model, &gate](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { nmos::resources& resources = model.control_protocol_resources; @@ -1781,7 +1781,7 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo // Error return web::json::value::array(); } - + if (!nmos::nc::is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)))) { // Error @@ -1820,7 +1820,6 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo if (touchpoint_resource != resources.end()) { - const auto lock = model.write_lock(); bool success = erase_resource(model.node_resources, nmos::fields::id(touchpoint_resource->data)); if (!success) @@ -1919,7 +1918,7 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo if (touchpoint_property_holder == web::json::value::null()) { - // Error + // Error continue; } const auto& oid2 = nmos::fields::nc::value(oid_property_holder); @@ -1978,7 +1977,7 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo const auto& owner = nmos::fields::nc::value(owner_property_holder).as_integer(); const auto& role = nmos::fields::nc::value(role_property_holder).as_string(); - + auto receiver_monitor = nmos::make_receiver_monitor(oid2.as_integer(), true, owner, role, U(""), U(""), web::json::value_of({{nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})}})); auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U(""), role, oid2.as_integer(), true, nmos::nc_receiver_monitor_class_id, U(""), owner); diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 7138298d4..ff7f1b805 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -628,13 +628,15 @@ namespace nmos const auto role_path = parameters.at(nmos::patterns::rolePath.name); const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - auto lock = model.read_lock(); - auto& resources = model.control_protocol_resources; - const auto& resource = nc::find_resource_by_role_path(resources, role_path); - if (resources.end() != resource) + return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, version, &gate_](value body) mutable { - return details::extract_json(req, gate_).then([res, resources, resource, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, version, &gate_](value body) mutable + auto& resources = model.control_protocol_resources; + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + + if (resources.end() != resource) { + auto lock = model.write_lock(); + // Validate JSON syntax according to the schema details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_validate_request_schema_uri(version)); @@ -666,15 +668,14 @@ namespace nmos code = status_codes::BadRequest; } set_reply(res, code, method_result); - - return true; - }); - } - else - { - // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); - } + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } + return true; + }); return pplx::task_from_result(true); }); @@ -685,12 +686,12 @@ namespace nmos const auto role_path = parameters.at(nmos::patterns::rolePath.name); const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - auto lock = model.read_lock(); - auto& resources = model.control_protocol_resources; - const auto& resource = nc::find_resource_by_role_path(resources, role_path); - if (resources.end() != resource) + return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, version, &gate_](value body) mutable { - return details::extract_json(req, gate_).then([res, &resources, resource, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, version, &model, &gate_](value body) mutable + auto lock = model.write_lock(); + auto& resources = model.control_protocol_resources; + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) { // Validate JSON syntax according to the schema details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_set_request_schema_uri(version)); @@ -719,15 +720,15 @@ namespace nmos code = status_codes::BadRequest; } set_reply(res, code, method_result); + } + else + { + // resource not found for the role path + set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + } - return true; - }); - } - else - { - // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); - } + return true; + }); return pplx::task_from_result(true); }); diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h index 7d44337b0..7ca6940e7 100644 --- a/Development/nmos/configuration_handlers.h +++ b/Development/nmos/configuration_handlers.h @@ -19,12 +19,12 @@ namespace nmos } // This callback is invoked if attempting to modify read only properties when restoring a configuration. // This function should modify the Device Model object directly and return a corresponding NcObjectPropertiesSetValidation object - typedef std::function filter_property_value_holders_handler; + typedef std::function filter_property_value_holders_handler; // This callback is invoked if attempting to modify a rebuildable block when restoring a configuration. // This function should handle the modification of the Device Model and any corresponding NMOS resources // and return correpsonding NcObjectPropertiesSetValidation objects for each object modified/added - typedef std::function modify_rebuildable_block_handler; + typedef std::function modify_rebuildable_block_handler; } #endif diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index f78e845f6..d95bfa293 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -196,7 +196,7 @@ namespace nmos if (modify_rebuildable_block) { // call back to application code which will return an object_properties_set_validation_values object - return modify_rebuildable_block(resource, target_role_path, child_object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor); + return modify_rebuildable_block(resource, target_role_path, child_object_properties_holders, recurse, validate, get_control_protocol_class_descriptor); } else { @@ -251,10 +251,10 @@ namespace nmos { if (filter_property_value_holders) { - // If the property_modify_list contains read only properties then we call back to the application code to + // If the property_modify_list contains read only properties then we call back to the application code to // check that it's OK to change those value. Bear in mind that they could be the class Id, or the oid or some other // property that we don't want changed - property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list, recurse, restore_mode, validate, property_restore_notices.as_array(), get_control_protocol_class_descriptor); + property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list, recurse, validate, property_restore_notices.as_array(), get_control_protocol_class_descriptor); } else { diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index f17627ebd..d0e41dd42 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -289,7 +289,7 @@ BST_TEST_CASE(testGetChildObjectPropertiesHolders) // Create Object Properties Holder auto object_properties_holders = value::array(); - + { const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_value_holders = value::array(); @@ -445,7 +445,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool modify_rebuildable_block_called = false; // callback stubs - nmos::filter_property_value_holders_handler filter_property_value_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, const web::json::value& restore_mode, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::filter_property_value_holders_handler filter_property_value_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { filter_property_value_holders_called = true; auto modifiable_property_value_holders = value::array(); @@ -456,7 +456,7 @@ BST_TEST_CASE(testApplyBackupDataSet) } return modifiable_property_value_holders.as_array(); }; - nmos::modify_rebuildable_block_handler modify_rebuildable_block = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::modify_rebuildable_block_handler modify_rebuildable_block = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { modify_rebuildable_block_called = true; auto out = value::array(); From d9813cf598204f8eaad691b35e471b40a48a1b13 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 27 Jan 2025 14:38:33 +0000 Subject: [PATCH 167/250] Add configuration_resources module --- Development/cmake/NmosCppLibraries.cmake | 2 ++ Development/cmake/NmosCppTest.cmake | 2 ++ Development/nmos/configuration_resources.cpp | 18 ++++++++++++++++++ Development/nmos/configuration_resources.h | 13 +++++++++++++ .../nmos/test/configuration_resources_test.cpp | 11 +++++++++++ 5 files changed, 46 insertions(+) create mode 100644 Development/nmos/configuration_resources.cpp create mode 100644 Development/nmos/configuration_resources.h create mode 100644 Development/nmos/test/configuration_resources_test.cpp diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index 80a9491bc..811232b69 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -1007,6 +1007,7 @@ set(NMOS_CPP_NMOS_SOURCES nmos/components.cpp nmos/configuration_api.cpp nmos/configuration_methods.cpp + nmos/configuration_resources.cpp nmos/configuration_utils.cpp nmos/connection_activation.cpp nmos/connection_api.cpp @@ -1104,6 +1105,7 @@ set(NMOS_CPP_NMOS_HEADERS nmos/configuration_api.h nmos/configuration_handlers.h nmos/configuration_methods.h + nmos/configuration_resources.h nmos/configuration_utils.h nmos/connection_activation.h nmos/connection_api.h diff --git a/Development/cmake/NmosCppTest.cmake b/Development/cmake/NmosCppTest.cmake index 2ff493779..3717a4e01 100644 --- a/Development/cmake/NmosCppTest.cmake +++ b/Development/cmake/NmosCppTest.cmake @@ -43,8 +43,10 @@ set(NMOS_CPP_TEST_NMOS_TEST_SOURCES nmos/test/api_utils_test.cpp nmos/test/capabilities_test.cpp nmos/test/channels_test.cpp + nmos/test/configuration_resources_test.cpp nmos/test/configuration_utils_test.cpp nmos/test/control_protocol_test.cpp + nmos/test/control_protocol_test.cpp nmos/test/control_protocol_utils_test.cpp nmos/test/did_sdid_test.cpp nmos/test/event_type_test.cpp diff --git a/Development/nmos/configuration_resources.cpp b/Development/nmos/configuration_resources.cpp new file mode 100644 index 000000000..358d73847 --- /dev/null +++ b/Development/nmos/configuration_resources.cpp @@ -0,0 +1,18 @@ +#include "nmos/configuration_methods.h" + +#include +#include "cpprest/json_utils.h" +#include "nmos/configuration_handlers.h" +#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_resources.h" +#include "nmos/control_protocol_state.h" +#include "nmos/control_protocol_utils.h" +#include "nmos/slog.h" + +namespace nmos +{ + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::array& notices, const utility::string_t& statusMessage) + { + return web::json::value(); + } +} diff --git a/Development/nmos/configuration_resources.h b/Development/nmos/configuration_resources.h new file mode 100644 index 000000000..abd7af542 --- /dev/null +++ b/Development/nmos/configuration_resources.h @@ -0,0 +1,13 @@ +#ifndef NMOS_CONFIGURATION_RESOURCES_H +#define NMOS_CONFIGURATION_RESOURCES_H + +#include "nmos/configuration_handlers.h" +#include "nmos/control_protocol_handlers.h" +#include "nmos/resources.h" + +namespace nmos +{ + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::array& notices, const utility::string_t& statusMessage); +} + +#endif diff --git a/Development/nmos/test/configuration_resources_test.cpp b/Development/nmos/test/configuration_resources_test.cpp new file mode 100644 index 000000000..ab86254ae --- /dev/null +++ b/Development/nmos/test/configuration_resources_test.cpp @@ -0,0 +1,11 @@ +// The first "test" is of course whether the header compiles standalone +#include "nmos/control_protocol_typedefs.h" +#include "nmos/configuration_resources.h" + +#include "bst/test/test.h" + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testMakeObjectPropertiesSetValidation) +{ + +} From 27ac4519701ee77e6dcf657841fb9f7ff6c8259b Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 27 Jan 2025 16:10:53 +0000 Subject: [PATCH 168/250] Add configuration_resources module. Add return objects to callback function --- .../nmos-cpp-node/node_implementation.cpp | 102 +++++++++++++----- Development/nmos/configuration_methods.cpp | 2 +- Development/nmos/configuration_resources.cpp | 42 ++++++-- Development/nmos/configuration_resources.h | 11 +- Development/nmos/json_fields.h | 2 +- .../test/configuration_resources_test.cpp | 61 ++++++++++- 6 files changed, 178 insertions(+), 42 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 0b8d207b5..2c55939d5 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -21,6 +21,7 @@ #include "nmos/colorspace.h" #include "nmos/configuration_handlers.h" #include "nmos/configuration_methods.h" +#include "nmos/configuration_resources.h" #include "nmos/configuration_utils.h" #include "nmos/connection_resources.h" #include "nmos/connection_events_activation.h" @@ -1766,6 +1767,8 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo { return [&model, &gate](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { + auto object_properties_set_validations = web::json::value::array(); + nmos::resources& resources = model.control_protocol_resources; slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do modify_rebuildable_block"; @@ -1777,22 +1780,30 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo if (filtered_holders.size() != 1) { - // Either can't find associated object_properties_holder, or there's more than one (ambiguous) - // Error - return web::json::value::array(); + auto status_message = U("Either can't find associated object_properties_holder, or there's more than one (ambiguous)"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + return object_properties_set_validations; } if (!nmos::nc::is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)))) { - // Error - return web::json::value::array(); + auto status_message = U("Expected an NcBlock"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + return object_properties_set_validations; } const auto& block_members_properties_holder = nmos::get_property_value_holder(*filtered_holders.begin(), nmos::nc_property_id(2, 2)); if (block_members_properties_holder == web::json::value::null()) { - // Error - return web::json::value::array(); + auto status_message = U("No NcBlockMembersPropertiesHolder found"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + return object_properties_set_validations; } const auto& restore_members = nmos::fields::nc::value(block_members_properties_holder); @@ -1804,6 +1815,9 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo // Iterate through the members of the block and compare to the members in the backup dataset for (const auto& reference_member : reference_members) { + auto child_role_path = web::json::value_from_elements(target_role_path); + web::json::push_back(child_role_path, nmos::fields::nc::role(reference_member)); + const auto& filtered_members = boost::copy_range>(restore_members.as_array() | boost::adaptors::filtered([&reference_member](const web::json::value& member) { @@ -1824,7 +1838,10 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo if (!success) { - // Error + auto status_message = U("Unable to erase resource"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + continue; } const auto oid = nmos::fields::nc::oid(found->data); @@ -1832,6 +1849,8 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo if (success) { members_to_remove.push_back(oid); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); } } } @@ -1847,12 +1866,23 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo // Modify existing resource // in this example we will ignore/reject changes to the role, constant_oid, class_id or owner // Do nothing, return warning + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); continue; } + else + { + // Do nothing + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + } } } for (const auto& restore_member : restore_members.as_array()) { + auto child_role_path = web::json::value_from_elements(target_role_path); + web::json::push_back(child_role_path, nmos::fields::nc::role(restore_member)); + const auto& filtered_members = boost::copy_range>(reference_members | boost::adaptors::filtered([&restore_member](const web::json::value& member) { @@ -1866,7 +1896,10 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo // Get example resource from the exising members to get node_id, device_id if (reference_members.size() == 0) { - // Error + auto status_message = U("Cannot duplicate resources when none exist"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + continue; } const auto& example_monitor = *reference_members.begin(); @@ -1874,22 +1907,14 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo const auto& touchpoint_resource = nmos::nc::find_touchpoint_resource(model.node_resources, *found); if (touchpoint_resource == resources.end()) { - // Error + auto status_message = U("Cannot duplicate resources when none exist"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + continue; } const auto& device_id = nmos::fields::device_id(touchpoint_resource->data); - // calculate child resource role path - const auto& target_role_path_ = nmos::get_role_path(resources, resource); - - // Hmmmm, there must be a better way of appending the child role to the end of the target role path array... - auto child_role_path = web::json::value::array(); - for (const auto& path_element : target_role_path_) - { - web::json::push_back(child_role_path, path_element); - } - web::json::push_back(child_role_path, nmos::fields::nc::role(restore_member)); - // Find the object_properties_holder that describes the new receiver monitor const auto& filtered_child_object_properties_holders = boost::copy_range>(object_properties_holders | boost::adaptors::filtered([&child_role_path](const web::json::value& object_properties_holder) @@ -1900,7 +1925,10 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo if (filtered_child_object_properties_holders.size() != 1) { - // Error + auto status_message = U("Cannot find NcObjectPropertiesHolder for new resource"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + continue; } @@ -1910,7 +1938,10 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo if (oid_property_holder == web::json::value::null()) { - // Error + auto status_message = U("Cannot find OID object property value holder"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + continue; } @@ -1918,7 +1949,10 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo if (touchpoint_property_holder == web::json::value::null()) { - // Error + auto status_message = U("Cannot find touchpoint object property value holder"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + continue; } const auto& oid2 = nmos::fields::nc::value(oid_property_holder); @@ -1927,7 +1961,10 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo if (touchpoints.size() != 1) { - // Error + auto status_message = U("Either zero or more than one touchpoint found (ambiguous)."); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + continue; } const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); @@ -1964,14 +2001,20 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo const auto& owner_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_property_id(1, 4)); if (owner_property_holder == web::json::value::null()) { - // Error + auto status_message = U("Cannot find owner property value holder."); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + continue; } const auto& role_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_property_id(1, 5)); if (role_property_holder == web::json::value::null()) { - // Error + auto status_message = U("Cannot find role property value holder."); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + continue; } @@ -1986,6 +2029,9 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo // insert resources insert_resource(model.node_resources, std::move(receiver)); insert_resource(resources, std::move(receiver_monitor)); + + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); } } @@ -2019,7 +2065,7 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { { nmos::nc_property_id(2, 2), nmos::nc_property_change_type::type::value_changed, modified_members } })); } - return web::json::value::array(); + return object_properties_set_validations; }; } diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index e0631c55e..1f9b45885 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -43,7 +43,7 @@ namespace nmos using web::json::value; // Get property_value_holders for this resource - const auto property_value_holders = make_property_value_holders(resource, get_control_protocol_class_descriptor).as_array(); + const auto& property_value_holders = make_property_value_holders(resource, get_control_protocol_class_descriptor).as_array(); const auto role_path = get_role_path(resources, resource); diff --git a/Development/nmos/configuration_resources.cpp b/Development/nmos/configuration_resources.cpp index 358d73847..24979696c 100644 --- a/Development/nmos/configuration_resources.cpp +++ b/Development/nmos/configuration_resources.cpp @@ -1,18 +1,44 @@ -#include "nmos/configuration_methods.h" +#include "nmos/configuration_resources.h" -#include #include "cpprest/json_utils.h" -#include "nmos/configuration_handlers.h" #include "nmos/control_protocol_resource.h" -#include "nmos/control_protocol_resources.h" -#include "nmos/control_protocol_state.h" -#include "nmos/control_protocol_utils.h" #include "nmos/slog.h" namespace nmos { - web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::array& notices, const utility::string_t& statusMessage) + namespace details { - return web::json::value(); + web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::value& notices, const web::json::value& status_message) + { + using web::json::value; + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::path, web::json::value_from_elements(role_path)}, + { nmos::fields::nc::status, status }, + { nmos::fields::nc::notices, notices }, + { nmos::fields::nc::status_message, status_message } + }); + } + } + + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::value& notices, const utility::string_t& status_message) + { + return details::make_nc_object_properties_set_validation(role_path, status, notices, web::json::value::string(status_message)); + } + + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::value& notices) + { + return details::make_nc_object_properties_set_validation(role_path, status, notices, web::json::value::null()); + } + + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status) + { + return details::make_nc_object_properties_set_validation(role_path, status, web::json::value::array(), web::json::value::null()); + } + + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const utility::string_t& status_message) + { + return details::make_nc_object_properties_set_validation(role_path, status, web::json::value::array(), web::json::value::string(status_message)); } } diff --git a/Development/nmos/configuration_resources.h b/Development/nmos/configuration_resources.h index abd7af542..22040dbf0 100644 --- a/Development/nmos/configuration_resources.h +++ b/Development/nmos/configuration_resources.h @@ -1,13 +1,18 @@ #ifndef NMOS_CONFIGURATION_RESOURCES_H #define NMOS_CONFIGURATION_RESOURCES_H -#include "nmos/configuration_handlers.h" -#include "nmos/control_protocol_handlers.h" +#include "nmos/control_protocol_typedefs.h" #include "nmos/resources.h" namespace nmos { - web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::array& notices, const utility::string_t& statusMessage); + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::value& notices, const utility::string_t& status_message); + + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::value& notices); + + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status); + + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const utility::string_t& status_message); } #endif diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index 47caed188..b4d3b78d7 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -338,7 +338,7 @@ namespace nmos const web::json::field_as_bool active{ U("active") }; const web::json::field_as_array values{ U("values") }; const web::json::field_as_string validation_fingerprint{ U("validationFingerprint") }; - const web::json::field_as_string status_message{ U("statusMessage") }; + const web::json::field_as_value status_message{ U("statusMessage") }; const web::json::field_as_value data_set{ U("dataSet") }; // NcBulkValuesHolder const web::json::field_as_bool is_rebuildable{ U("isRebuildable") }; const web::json::field_as_integer notice_type{ U("noticeType") }; diff --git a/Development/nmos/test/configuration_resources_test.cpp b/Development/nmos/test/configuration_resources_test.cpp index ab86254ae..287764966 100644 --- a/Development/nmos/test/configuration_resources_test.cpp +++ b/Development/nmos/test/configuration_resources_test.cpp @@ -7,5 +7,64 @@ //////////////////////////////////////////////////////////////////////////////////////////// BST_TEST_CASE(testMakeObjectPropertiesSetValidation) { - + using web::json::value_of; + using web::json::value; + + auto role_path = web::json::value_of({ U("root"), U("path1") }).as_array(); + auto status = nmos::nc_restore_validation_status::ok; + auto notices = value::array(); + auto status_message = U("status message"); + + { + auto object_properties_set_validation = nmos::make_object_properties_set_validation(role_path, status, notices, status_message); + + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::path)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::status)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::notices)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::status_message)); + + BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(status, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(notices.as_array(), nmos::fields::nc::notices(object_properties_set_validation)); + BST_CHECK_EQUAL(value::string(status_message), nmos::fields::nc::status_message(object_properties_set_validation)); + } + { + auto object_properties_set_validation = nmos::make_object_properties_set_validation(role_path, status, notices); + + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::path)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::status)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::notices)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::status_message)); + + BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(status, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(notices.as_array(), nmos::fields::nc::notices(object_properties_set_validation)); + BST_CHECK_EQUAL(web::json::value::null(), nmos::fields::nc::status_message(object_properties_set_validation)); + } + { + auto object_properties_set_validation = nmos::make_object_properties_set_validation(role_path, status); + + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::path)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::status)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::notices)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::status_message)); + + BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(status, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(web::json::value::array().as_array(), nmos::fields::nc::notices(object_properties_set_validation)); + BST_CHECK_EQUAL(web::json::value::null(), nmos::fields::nc::status_message(object_properties_set_validation)); + } + { + auto object_properties_set_validation = nmos::make_object_properties_set_validation(role_path, status, status_message); + + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::path)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::status)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::notices)); + BST_REQUIRE(object_properties_set_validation.has_field(nmos::fields::nc::status_message)); + + BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(status, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(web::json::value::array().as_array(), nmos::fields::nc::notices(object_properties_set_validation)); + BST_CHECK_EQUAL(value::string(status_message), nmos::fields::nc::status_message(object_properties_set_validation)); + } } From 098facfc3479b399e74ae4ab75040872dd915f2a Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 27 Jan 2025 16:32:30 +0000 Subject: [PATCH 169/250] Refactor make ObjectPropertiesSetValidation object creation --- Development/nmos/configuration_resources.cpp | 24 ++++--------------- Development/nmos/configuration_resources.h | 4 ++-- Development/nmos/configuration_utils.cpp | 13 +++++----- .../nmos/control_protocol_resource.cpp | 12 +++++----- Development/nmos/control_protocol_resource.h | 2 +- .../test/configuration_resources_test.cpp | 6 ++--- .../nmos/test/configuration_utils_test.cpp | 3 ++- 7 files changed, 25 insertions(+), 39 deletions(-) diff --git a/Development/nmos/configuration_resources.cpp b/Development/nmos/configuration_resources.cpp index 24979696c..949fa3fd9 100644 --- a/Development/nmos/configuration_resources.cpp +++ b/Development/nmos/configuration_resources.cpp @@ -6,39 +6,23 @@ namespace nmos { - namespace details - { - web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::value& notices, const web::json::value& status_message) - { - using web::json::value; - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::path, web::json::value_from_elements(role_path)}, - { nmos::fields::nc::status, status }, - { nmos::fields::nc::notices, notices }, - { nmos::fields::nc::status_message, status_message } - }); - } - } - - web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::value& notices, const utility::string_t& status_message) + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::array& notices, const utility::string_t& status_message) { return details::make_nc_object_properties_set_validation(role_path, status, notices, web::json::value::string(status_message)); } - web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::value& notices) + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::array& notices) { return details::make_nc_object_properties_set_validation(role_path, status, notices, web::json::value::null()); } web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status) { - return details::make_nc_object_properties_set_validation(role_path, status, web::json::value::array(), web::json::value::null()); + return details::make_nc_object_properties_set_validation(role_path, status, web::json::value::array().as_array(), web::json::value::null()); } web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const utility::string_t& status_message) { - return details::make_nc_object_properties_set_validation(role_path, status, web::json::value::array(), web::json::value::string(status_message)); + return details::make_nc_object_properties_set_validation(role_path, status, web::json::value::array().as_array(), web::json::value::string(status_message)); } } diff --git a/Development/nmos/configuration_resources.h b/Development/nmos/configuration_resources.h index 22040dbf0..c4569233d 100644 --- a/Development/nmos/configuration_resources.h +++ b/Development/nmos/configuration_resources.h @@ -6,9 +6,9 @@ namespace nmos { - web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::value& notices, const utility::string_t& status_message); + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::array& notices, const utility::string_t& status_message); - web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::value& notices); + web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::array& notices); web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status); diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index d95bfa293..f6a72e28d 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -3,6 +3,7 @@ #include #include "cpprest/json_utils.h" #include "nmos/configuration_handlers.h" +#include "nmos/configuration_resources.h" #include "nmos/control_protocol_resource.h" #include "nmos/control_protocol_resources.h" #include "nmos/control_protocol_state.h" @@ -127,7 +128,7 @@ namespace nmos // can't find this oid, so member has been removed return true; } - const auto restore_member = *filtered_members.begin(); + const auto& restore_member = *filtered_members.begin(); // We ignore the description and user label as these are non-normative if (nmos::fields::nc::role(reference_member) != nmos::fields::nc::role(restore_member) || nmos::fields::nc::constant_oid(reference_member) != nmos::fields::nc::constant_oid(restore_member) @@ -181,7 +182,7 @@ namespace nmos if (target_object_properties_holders.size() > 1) { // Error in the backup dataset - const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(nmos::fields::nc::path(*target_object_properties_holders.begin()), nmos::nc_restore_validation_status::failed, web::json::value::array().as_array(), U("more than one object_properties_holder for role path")); + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(nmos::fields::nc::path(*target_object_properties_holders.begin()), nmos::nc_restore_validation_status::failed, U("more than one object_properties_holder for role path")); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); return object_properties_set_validation_values; } @@ -201,7 +202,7 @@ namespace nmos else { // Rebuilding blocks not supported - const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, web::json::value::array().as_array(), U("Rebuilding of Device Model blocks not supported")); + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, U("Rebuilding of Device Model blocks not supported")); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); return object_properties_set_validation_values; } @@ -259,7 +260,7 @@ namespace nmos else { // Modify of read only properties not supported - const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, property_restore_notices.as_array(), U("Modification of read only properties not supported")); + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, property_restore_notices.as_array(), U("Modification of read only properties not supported")); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); continue; } @@ -282,7 +283,7 @@ namespace nmos }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { {property_id, nmos::nc_property_change_type::type::value_changed, value} })); } } - const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, property_restore_notices.as_array(), U("OK")); + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, property_restore_notices.as_array(), U("OK")); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); } @@ -335,7 +336,7 @@ namespace nmos ); for (const auto& orphan_object_properties_holder : orphan_object_properties_holders) { - const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(nmos::fields::nc::path(orphan_object_properties_holder), nmos::nc_restore_validation_status::not_found, web::json::value::array().as_array(), U("object role path not found under target role path")); + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(nmos::fields::nc::path(orphan_object_properties_holder), nmos::nc_restore_validation_status::not_found, U("object role path not found under target role path")); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); } diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 22b4c0ac3..5eb183053 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -713,7 +713,7 @@ namespace nmos data[nmos::fields::nc::runtime_property_constraints] = runtime_property_constraints; // level 2 runtime constraints. See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html // IS-14 isRebuilable flag - // use make_rebuildable function to declare an control protocl resource rebuildable + // use make_rebuildable function to declare an control protocl resource rebuildable data[nmos::fields::nc::is_rebuildable] = value::boolean(false); return data; } @@ -858,7 +858,7 @@ namespace nmos // TODO: add link web::json::value make_nc_property_value_holder(const nc_property_id& property_id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, const web::json::value& property_value) { - using web::json::value; + using web::json::value; using web::json::value_of; return value_of({ @@ -892,7 +892,7 @@ namespace nmos return value_of({ { nmos::fields::nc::id, make_nc_property_id(property_id)}, - { nmos::fields::nc::name, value::string(name)}, + { nmos::fields::nc::name, value::string(name)}, { nmos::fields::nc::notice_type, value::number(notice_type)}, { nmos::fields::nc::notice_message, value::string(notice_message)} }, true @@ -900,16 +900,16 @@ namespace nmos } // TODO: add link - web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const utility::string_t& status_message) + web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const web::json::value& status_message) { using web::json::value; using web::json::value_of; - return value_of({ + return value_of({ { nmos::fields::nc::path, web::json::value_from_elements(role_path)}, { nmos::fields::nc::status, value::number(status)}, { nmos::fields::nc::notices, web::json::value_from_elements(notices)}, - { nmos::fields::nc::status_message, value::string(status_message)} + { nmos::fields::nc::status_message, status_message} }, true ); } diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index e2b74f45d..632cd7ab6 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -208,7 +208,7 @@ namespace nmos web::json::value make_nc_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message); // TODO: add link - web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const utility::string_t& status_message); + web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const web::json::value& status_message); } // command message response diff --git a/Development/nmos/test/configuration_resources_test.cpp b/Development/nmos/test/configuration_resources_test.cpp index 287764966..b1950b209 100644 --- a/Development/nmos/test/configuration_resources_test.cpp +++ b/Development/nmos/test/configuration_resources_test.cpp @@ -12,7 +12,7 @@ BST_TEST_CASE(testMakeObjectPropertiesSetValidation) auto role_path = web::json::value_of({ U("root"), U("path1") }).as_array(); auto status = nmos::nc_restore_validation_status::ok; - auto notices = value::array(); + auto notices = value::array().as_array(); auto status_message = U("status message"); { @@ -25,7 +25,7 @@ BST_TEST_CASE(testMakeObjectPropertiesSetValidation) BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); BST_CHECK_EQUAL(status, nmos::fields::nc::status(object_properties_set_validation)); - BST_CHECK_EQUAL(notices.as_array(), nmos::fields::nc::notices(object_properties_set_validation)); + BST_CHECK_EQUAL(notices, nmos::fields::nc::notices(object_properties_set_validation)); BST_CHECK_EQUAL(value::string(status_message), nmos::fields::nc::status_message(object_properties_set_validation)); } { @@ -38,7 +38,7 @@ BST_TEST_CASE(testMakeObjectPropertiesSetValidation) BST_CHECK_EQUAL(role_path, nmos::fields::nc::path(object_properties_set_validation)); BST_CHECK_EQUAL(status, nmos::fields::nc::status(object_properties_set_validation)); - BST_CHECK_EQUAL(notices.as_array(), nmos::fields::nc::notices(object_properties_set_validation)); + BST_CHECK_EQUAL(notices, nmos::fields::nc::notices(object_properties_set_validation)); BST_CHECK_EQUAL(web::json::value::null(), nmos::fields::nc::status_message(object_properties_set_validation)); } { diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index d0e41dd42..5bd4b81f1 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -5,6 +5,7 @@ #include "nmos/control_protocol_typedefs.h" #include "nmos/control_protocol_utils.h" #include "nmos/configuration_handlers.h" +#include "nmos/configuration_resources.h" #include "nmos/configuration_utils.h" #include "bst/test/test.h" @@ -460,7 +461,7 @@ BST_TEST_CASE(testApplyBackupDataSet) { modify_rebuildable_block_called = true; auto out = value::array(); - const auto& object_properties_set_validation = nmos::details::make_nc_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, value::array().as_array(), U("OK")); + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, U("OK")); web::json::push_back(out, object_properties_set_validation); return out; }; From ec0643bad46428ceda097020a2c4013475350d27 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe <64410119+jonathan-r-thorpe@users.noreply.github.com> Date: Wed, 29 Jan 2025 14:11:50 +0000 Subject: [PATCH 170/250] Apply suggestions from code review Co-authored-by: Simon Lo --- Development/cmake/NmosCppLibraries.cmake | 4 ++-- Development/cmake/NmosCppTest.cmake | 6 +++--- Development/nmos/configuration_api.cpp | 4 +--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index 811232b69..81ab44fa8 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -1007,7 +1007,7 @@ set(NMOS_CPP_NMOS_SOURCES nmos/components.cpp nmos/configuration_api.cpp nmos/configuration_methods.cpp - nmos/configuration_resources.cpp + nmos/configuration_resources.cpp nmos/configuration_utils.cpp nmos/connection_activation.cpp nmos/connection_api.cpp @@ -1105,7 +1105,7 @@ set(NMOS_CPP_NMOS_HEADERS nmos/configuration_api.h nmos/configuration_handlers.h nmos/configuration_methods.h - nmos/configuration_resources.h + nmos/configuration_resources.h nmos/configuration_utils.h nmos/connection_activation.h nmos/connection_api.h diff --git a/Development/cmake/NmosCppTest.cmake b/Development/cmake/NmosCppTest.cmake index 3717a4e01..567880afb 100644 --- a/Development/cmake/NmosCppTest.cmake +++ b/Development/cmake/NmosCppTest.cmake @@ -43,11 +43,11 @@ set(NMOS_CPP_TEST_NMOS_TEST_SOURCES nmos/test/api_utils_test.cpp nmos/test/capabilities_test.cpp nmos/test/channels_test.cpp - nmos/test/configuration_resources_test.cpp + nmos/test/configuration_resources_test.cpp nmos/test/configuration_utils_test.cpp nmos/test/control_protocol_test.cpp - nmos/test/control_protocol_test.cpp - nmos/test/control_protocol_utils_test.cpp + nmos/test/control_protocol_test.cpp + nmos/test/control_protocol_utils_test.cpp nmos/test/did_sdid_test.cpp nmos/test/event_type_test.cpp nmos/test/json_validator_test.cpp diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index ff7f1b805..88a4440fa 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -630,13 +630,11 @@ namespace nmos return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, version, &gate_](value body) mutable { + auto lock = model.write_lock(); auto& resources = model.control_protocol_resources; const auto& resource = nc::find_resource_by_role_path(resources, role_path); - if (resources.end() != resource) { - auto lock = model.write_lock(); - // Validate JSON syntax according to the schema details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_validate_request_schema_uri(version)); From 38a424ee76e591cac21608cbc71a85ee2b47b10f Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 29 Jan 2025 15:54:00 +0000 Subject: [PATCH 171/250] Fixed crash in get_properties_by_path --- Development/cmake/NmosCppTest.cmake | 1 + Development/nmos/configuration_methods.cpp | 7 +- .../nmos/test/configuration_methods_test.cpp | 93 +++++++++++++++++++ 3 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 Development/nmos/test/configuration_methods_test.cpp diff --git a/Development/cmake/NmosCppTest.cmake b/Development/cmake/NmosCppTest.cmake index 567880afb..b0a1996ca 100644 --- a/Development/cmake/NmosCppTest.cmake +++ b/Development/cmake/NmosCppTest.cmake @@ -43,6 +43,7 @@ set(NMOS_CPP_TEST_NMOS_TEST_SOURCES nmos/test/api_utils_test.cpp nmos/test/capabilities_test.cpp nmos/test/channels_test.cpp + nmos/test/configuration_methods_test.cpp nmos/test/configuration_resources_test.cpp nmos/test/configuration_utils_test.cpp nmos/test/control_protocol_test.cpp diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index 1f9b45885..9e3b39a85 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -8,13 +8,12 @@ #include "nmos/control_protocol_resources.h" #include "nmos/control_protocol_state.h" #include "nmos/control_protocol_utils.h" -#include "nmos/slog.h" namespace nmos { namespace details { - web::json::value make_property_value_holders(const nmos::resource& resource, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + web::json::array make_property_value_holders(const nmos::resource& resource, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { using web::json::value; @@ -35,7 +34,7 @@ namespace nmos } class_id.pop_back(); } - return property_value_holders; + return property_value_holders.as_array(); } void populate_object_property_holder(const nmos::resources& resources, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const nmos::resource& resource, bool recurse, web::json::value& object_properties_holders) @@ -43,7 +42,7 @@ namespace nmos using web::json::value; // Get property_value_holders for this resource - const auto& property_value_holders = make_property_value_holders(resource, get_control_protocol_class_descriptor).as_array(); + const auto& property_value_holders = make_property_value_holders(resource, get_control_protocol_class_descriptor); const auto role_path = get_role_path(resources, resource); diff --git a/Development/nmos/test/configuration_methods_test.cpp b/Development/nmos/test/configuration_methods_test.cpp new file mode 100644 index 000000000..b9c8a4b68 --- /dev/null +++ b/Development/nmos/test/configuration_methods_test.cpp @@ -0,0 +1,93 @@ +// The first "test" is of course whether the header compiles standalone +#include "nmos/control_protocol_resource.h" +#include "nmos/control_protocol_resources.h" +#include "nmos/control_protocol_state.h" +#include "nmos/control_protocol_typedefs.h" +#include "nmos/control_protocol_utils.h" +#include "nmos/configuration_handlers.h" +#include "nmos/configuration_resources.h" +#include "nmos/configuration_methods.h" +#include "nmos/configuration_utils.h" + +#include "bst/test/test.h" + + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testGetPropertiesByPath) +{ + using web::json::value_of; + using web::json::value; + + nmos::resources resources; + nmos::experimental::control_protocol_state control_protocol_state; + nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor = nmos::make_get_control_protocol_class_descriptor_handler(control_protocol_state); + nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor = nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state); + + // Create Device Model + // root + auto root_block = nmos::make_root_block(); + auto oid = nmos::root_block_oid; + // root, ClassManager + auto class_manager = nmos::make_class_manager(++oid, control_protocol_state); + auto receiver_block_oid = ++oid; + // root, receivers + auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); + nmos::make_rebuildable(receivers); + // root, receivers, mon1 + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({{nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); + // make monitor1 rebuildable + nmos::make_rebuildable(monitor1); + + auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + // root, receivers, mon2 + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + nmos::nc::push_back(receivers, monitor1); + // add example-control to root-block + nmos::nc::push_back(receivers, monitor2); + // add stereo-gain to root-block + nmos::nc::push_back(root_block, receivers); + // add class-manager to root-block + nmos::nc::push_back(root_block, class_manager); + insert_resource(resources, std::move(root_block)); + insert_resource(resources, std::move(class_manager)); + insert_resource(resources, std::move(receivers)); + insert_resource(resources, std::move(monitor1)); + insert_resource(resources, std::move(monitor2)); + + { + const auto target_role_path = value_of({ U("root") }); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + auto method_result = get_properties_by_path(resources, *resource, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + + BST_REQUIRE_EQUAL(nmos::nc_method_status::ok, nmos::fields::nc::status(method_result)); + + const auto& bulk_values_holder = nmos::fields::nc::value(method_result); + const auto& object_properties_holders = nmos::fields::nc::values(bulk_values_holder); + + BST_REQUIRE_EQUAL(5, object_properties_holders.size()); + } + { + const auto target_role_path = value_of({ U("root"), U("receivers") }); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + auto method_result = get_properties_by_path(resources, *resource, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + + BST_REQUIRE_EQUAL(nmos::nc_method_status::ok, nmos::fields::nc::status(method_result)); + + const auto& bulk_values_holder = nmos::fields::nc::value(method_result); + const auto& object_properties_holders = nmos::fields::nc::values(bulk_values_holder); + + BST_REQUIRE_EQUAL(3, object_properties_holders.size()); + } + { + const auto target_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + auto method_result = get_properties_by_path(resources, *resource, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + + BST_REQUIRE_EQUAL(nmos::nc_method_status::ok, nmos::fields::nc::status(method_result)); + + const auto& bulk_values_holder = nmos::fields::nc::value(method_result); + const auto& object_properties_holders = nmos::fields::nc::values(bulk_values_holder); + + BST_REQUIRE_EQUAL(1, object_properties_holders.size()); + } +} From 5eb174f66729c5e67999a31618268537fa039498 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Tue, 18 Mar 2025 14:18:35 +0000 Subject: [PATCH 172/250] Move insert and erase control protocol methods into nc namespace --- .../nmos-cpp-node/node_implementation.cpp | 2 +- Development/nmos/control_protocol_utils.cpp | 63 +++++++++---------- Development/nmos/control_protocol_utils.h | 16 ++--- .../test/control_protocol_methods_test.cpp | 12 ++-- 4 files changed, 42 insertions(+), 51 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index c4021f51d..8e2d56bba 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -291,7 +291,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const auto is_control_protocol_resource = [&resource]() { return nmos::types::all_nc.end() != std::find(nmos::types::all_nc.begin(), nmos::types::all_nc.end(), resource.type); }; const std::pair id_type{ resource.id, resource.type }; - const bool success = is_control_protocol_resource() ? insert_control_protocol_resource(resources, std::move(resource)).second : insert_resource(resources, std::move(resource)).second; + const bool success = is_control_protocol_resource() ? nmos::nc::insert_resource(resources, std::move(resource)).second : insert_resource(resources, std::move(resource)).second; if (success) slog::log(gate, SLOG_FLF) << "Updated model with " << id_type; diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 526513c94..0689ab42f 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -605,7 +605,23 @@ namespace nmos nc_block_resource.resources.push_back(resource); } - // *** insert_control_protocol_resource *** + // insert a control protocol resource + std::pair insert_resource(resources& resources, resource&& resource) + { + // set the creation and update timestamps, before inserting the resource + resource.updated = resource.created = nmos::strictly_increasing_update(resources); + + auto result = resources.insert(std::move(resource)); + // replacement of a deleted or expired resource is also allowed + // (currently, with no further checks on api_version, type, etc.) + if (!result.second && !result.first->has_data()) + { + // if the insertion was banned, resource has not been moved from + result.second = resources.replace(result.first, std::move(resource)); + } + return result; + } + // modify a control protocol resource, and insert notification event to all subscriptions bool modify_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event) @@ -654,7 +670,19 @@ namespace nmos return result; } - // *** erase_control_protocol_resource *** + // erase a control protocol resource + resources::size_type erase_resource(resources& resources, const id& id) + { + // hmm, may be also erasing all it's member blocks? + resources::size_type count = 0; + auto found = resources.find(id); + if (resources.end() != found && found->has_data()) + { + resources.erase(found); + ++count; + } + return count; + } // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id resources::const_iterator find_resource(resources& resources, type type, const id& resource_id) @@ -753,37 +781,6 @@ namespace nmos return nmos::find_resource(resources, touchpoint_uuid.as_string()); } } - - // insert a control protocol resource - std::pair insert_control_protocol_resource(resources& resources, resource&& resource) - { - // set the creation and update timestamps, before inserting the resource - resource.updated = resource.created = nmos::strictly_increasing_update(resources); - - auto result = resources.insert(std::move(resource)); - // replacement of a deleted or expired resource is also allowed - // (currently, with no further checks on api_version, type, etc.) - if (!result.second && !result.first->has_data()) - { - // if the insertion was banned, resource has not been moved from - result.second = resources.replace(result.first, std::move(resource)); - } - return result; - } - - // erase a control protocol resource - resources::size_type erase_control_protocol_resource(resources& resources, const id& id) - { - // hmm, may be also erasing all it's member blocks? - resources::size_type count = 0; - auto found = resources.find(id); - if (resources.end() != found && found->has_data()) - { - resources.erase(found); - ++count; - } - return count; - } // insert 'value changed', 'sequence item added', 'sequence item changed' or 'sequence item removed' notification events into all grains whose subscriptions match the specified version, type and "pre" or "post" values // this is used for the IS-12 propertry changed event diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index 76a63e0bb..044d26ca0 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -76,6 +76,9 @@ namespace nmos // push control protocol resource into other control protocol NcBlock resource void push_back(control_protocol_resource& nc_block_resource, const control_protocol_resource& resource); + // insert a control protocol resource + std::pair insert_resource(resources& resources, resource&& resource); + // modify a control protocol resource, and insert notification event to all subscriptions bool modify_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event); @@ -95,19 +98,10 @@ namespace nmos } // insert a control protocol resource - std::pair insert_control_protocol_resource(resources& resources, resource&& resource); - - // modify a control protocol resource, and insert notification event to all subscriptions - bool modify_control_protocol_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event); + //std::pair insert_control_protocol_resource(resources& resources, resource&& resource); // erase a control protocol resource - resources::size_type erase_control_protocol_resource(resources& resources, const id& id); - - // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id - resources::const_iterator find_control_protocol_resource(resources& resources, type type, const id& id); - - // method parameters constraints validation, may throw nmos::control_protocol_exception - void method_parameters_contraints_validation(const web::json::value& arguments, const web::json::value& nc_method_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor); + //resources::size_type erase_control_protocol_resource(resources& resources, const id& id); // insert 'value changed', 'sequence item added', 'sequence item changed' or 'sequence item removed' notification events into all grains whose subscriptions match the specified version, type and "pre" or "post" values void insert_notification_events(resources& resources, const api_version& version, const api_version& downgrade_version, const type& type, const web::json::value& pre, const web::json::value& post, const web::json::value& event); diff --git a/Development/nmos/test/control_protocol_methods_test.cpp b/Development/nmos/test/control_protocol_methods_test.cpp index d49370eed..2932f8cd7 100644 --- a/Development/nmos/test/control_protocol_methods_test.cpp +++ b/Development/nmos/test/control_protocol_methods_test.cpp @@ -38,7 +38,7 @@ BST_TEST_CASE(testRemoveSequenceItem) // Create simple non-standard class with writable sequence property - const auto writable_sequence_class_id = nmos::make_nc_class_id(nmos::nc_worker_class_id, -1234, { 1000 }); + const auto writable_sequence_class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, -1234, { 1000 }); const web::json::field_as_array writable_value{ U("writableValue") }; { // Writable sequence_class property descriptors @@ -82,15 +82,15 @@ BST_TEST_CASE(testRemoveSequenceItem) auto writable_sequence = make_writable_sequence(++oid, nmos::root_block_oid, U("writableSequence"), U("writable sequence"), U("writable sequence")); auto writable_sequence_id = writable_sequence.id; - nmos::push_back(receivers, monitor1); + nmos::nc::push_back(receivers, monitor1); // add example-control to root-block - nmos::push_back(receivers, monitor2); + nmos::nc::push_back(receivers, monitor2); // add stereo-gain to root-block - nmos::push_back(root_block, receivers); + nmos::nc::push_back(root_block, receivers); // add class-manager to root-block - nmos::push_back(root_block, class_manager); + nmos::nc::push_back(root_block, class_manager); // add writable sequence to root block - nmos::push_back(root_block, writable_sequence); + nmos::nc::push_back(root_block, writable_sequence); insert_resource(resources, std::move(root_block)); insert_resource(resources, std::move(class_manager)); insert_resource(resources, std::move(receivers)); From 327ab40728817351f0943751a78710b84d1acc6f Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 27 Mar 2025 13:36:34 +0000 Subject: [PATCH 173/250] Remove blank line --- Development/nmos/control_protocol_utils.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 0689ab42f..c75f755e1 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -622,7 +622,6 @@ namespace nmos return result; } - // modify a control protocol resource, and insert notification event to all subscriptions bool modify_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event) { From 710e9bd0b90670f01e64714446835d9e275c33ad Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 27 Mar 2025 13:37:15 +0000 Subject: [PATCH 174/250] Remove unreachable code --- Development/nmos/configuration_api.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 88a4440fa..441c1c992 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -674,8 +674,6 @@ namespace nmos } return true; }); - - return pplx::task_from_result(true); }); // PUT /rolePaths/{rolePath}/bulkProperties - invokes set_properties_by_path method @@ -727,8 +725,6 @@ namespace nmos return true; }); - - return pplx::task_from_result(true); }); return configuration_api; From 097ded603bdc7d719421e4ac506b0e3f3fff9fd1 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 27 Mar 2025 18:10:47 +0000 Subject: [PATCH 175/250] Add missing erase_resource for control_protocol_resource to header --- Development/nmos/control_protocol_utils.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index 044d26ca0..f5bc216e2 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -82,6 +82,9 @@ namespace nmos // modify a control protocol resource, and insert notification event to all subscriptions bool modify_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event); + // erase a control protocol resource + resources::size_type erase_resource(resources& resources, const id& id); + // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id resources::const_iterator find_resource(resources& resources, type type, const id& id); From 0dc1866e495fe8811e054d07e81b4bd0732c2954 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 27 Mar 2025 18:23:46 +0000 Subject: [PATCH 176/250] Use the relevant insert_resource and erase_resource for node_resource and control_protocol_resource --- .../nmos-cpp-node/node_implementation.cpp | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 8e2d56bba..8e1294499 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1776,14 +1776,15 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo { auto object_properties_set_validations = web::json::value::array(); - nmos::resources& resources = model.control_protocol_resources; + nmos::resources& control_protocol_resources = model.control_protocol_resources; + nmos::resources& node_resources = model.node_resources; slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do modify_rebuildable_block"; // Validate the object_properties_holder // Find object_properties_holder for resource - const auto& filtered_holders = nmos::get_object_properties_holder(object_properties_holders, nmos::get_role_path(resources, resource)); + const auto& filtered_holders = nmos::get_object_properties_holder(object_properties_holders, nmos::get_role_path(control_protocol_resources, resource)); if (filtered_holders.size() != 1) { @@ -1802,9 +1803,10 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo return object_properties_set_validations; } + const auto& block_members_properties_holder = nmos::get_property_value_holder(*filtered_holders.begin(), nmos::nc_property_id(2, 2)); - if (block_members_properties_holder == web::json::value::null()) + if (block_members_properties_holder.is_null()) { auto status_message = U("No NcBlockMembersPropertiesHolder found"); auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); @@ -1835,25 +1837,26 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo { // can't find this oid in restore dataset, so member has been removed // get the receiver monitor resource - auto found = nmos::find_resource(resources, utility::conversions::details::to_string_t(nmos::fields::nc::oid(reference_member))); + auto found = nmos::find_resource(control_protocol_resources, utility::conversions::details::to_string_t(nmos::fields::nc::oid(reference_member))); - const auto& touchpoint_resource = nmos::nc::find_touchpoint_resource(model.node_resources, *found); + const auto& touchpoint_resource = nmos::nc::find_touchpoint_resource(node_resources, *found); - if (touchpoint_resource != resources.end()) + if (touchpoint_resource != control_protocol_resources.end()) { - bool success = erase_resource(model.node_resources, nmos::fields::id(touchpoint_resource->data)); + const auto& id = nmos::fields::id(touchpoint_resource->data); + auto erase_count = erase_resource(node_resources, id); - if (!success) + if (erase_count == 0) { - auto status_message = U("Unable to erase resource"); + auto status_message = U("Unable to erase node resource ") + id; auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); web::json::push_back(object_properties_set_validations, object_properties_set_validation); continue; } const auto oid = nmos::fields::nc::oid(found->data); - success = erase_resource(resources, found->id); - if (success) + erase_count = nmos::nc::erase_resource(control_protocol_resources, found->id); + if (erase_count > 0) { members_to_remove.push_back(oid); auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); @@ -1910,9 +1913,9 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo continue; } const auto& example_monitor = *reference_members.begin(); - const auto& found = nmos::find_resource(resources, utility::conversions::details::to_string_t(nmos::fields::nc::oid(example_monitor))); - const auto& touchpoint_resource = nmos::nc::find_touchpoint_resource(model.node_resources, *found); - if (touchpoint_resource == resources.end()) + const auto& found = nmos::find_resource(control_protocol_resources, utility::conversions::details::to_string_t(nmos::fields::nc::oid(example_monitor))); + const auto& touchpoint_resource = nmos::nc::find_touchpoint_resource(node_resources, *found); + if (touchpoint_resource == control_protocol_resources.end()) { auto status_message = U("Cannot duplicate resources when none exist"); auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); @@ -2034,8 +2037,8 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo members_to_add.push_back(block_member_descriptor); // insert resources - insert_resource(model.node_resources, std::move(receiver)); - insert_resource(resources, std::move(receiver_monitor)); + insert_resource(node_resources, std::move(receiver)); + nmos::nc::insert_resource(control_protocol_resources, std::move(receiver_monitor)); auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); web::json::push_back(object_properties_set_validations, object_properties_set_validation); From cacbc0675601962b148ba83d8ac19d4a445dd100 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 27 Mar 2025 18:25:55 +0000 Subject: [PATCH 177/250] Replace magic propertry id numbers with defined propertry id numbers --- Development/nmos-cpp-node/node_implementation.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 8e1294499..a7fc0681e 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1944,7 +1944,7 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo const auto& child_object_properties_holder = *filtered_child_object_properties_holders.begin(); - const auto& oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_property_id(1, 2)); + const auto& oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_oid_property_id); if (oid_property_holder == web::json::value::null()) { @@ -2008,7 +2008,7 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo auto receiver = nmos::make_receiver(touchpoint_uuid.as_string(), device_id, nmos::transports::rtp, interface_names, model.settings); - const auto& owner_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_property_id(1, 4)); + const auto& owner_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_owner_property_id); if (owner_property_holder == web::json::value::null()) { auto status_message = U("Cannot find owner property value holder."); @@ -2018,7 +2018,7 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo continue; } - const auto& role_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_property_id(1, 5)); + const auto& role_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_role_property_id); if (role_property_holder == web::json::value::null()) { auto status_message = U("Cannot find role property value holder."); @@ -2068,11 +2068,11 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo web::json::push_back(modified_members, member); } - nmos::nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) - { - resource.data[nmos::fields::nc::members] = modified_members; + nmos::nc::modify_resource(control_protocol_resources, resource.id, [&](nmos::resource& resource) + { + resource.data[nmos::fields::nc::members] = modified_members; - }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { { nmos::nc_property_id(2, 2), nmos::nc_property_change_type::type::value_changed, modified_members } })); + }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { { nmos::nc_block_members_property_id, nmos::nc_property_change_type::type::value_changed, modified_members } })); } return object_properties_set_validations; From bca808d62a7c6ff325a2a1b9a1af39ad9d5d177a Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 19 May 2025 14:50:19 +0100 Subject: [PATCH 178/250] Update IS-14 datatypes to match latest changes to spec --- Development/nmos/configuration_methods.cpp | 2 +- .../nmos/control_protocol_resource.cpp | 6 ++- Development/nmos/control_protocol_resource.h | 2 +- Development/nmos/json_fields.h | 4 +- .../nmos/test/configuration_utils_test.cpp | 52 +++++++++---------- 5 files changed, 36 insertions(+), 30 deletions(-) diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index 9e3b39a85..6fc03f4ff 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -46,7 +46,7 @@ namespace nmos 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, nmos::fields::nc::is_rebuildable(resource.data)); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_value_holders, value::array().as_array(), value::array().as_array(), nmos::fields::nc::is_rebuildable(resource.data)); web::json::push_back(object_properties_holders, object_properties_holder); diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 5eb183053..0c051fb97 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -871,12 +871,14 @@ namespace nmos } // TODO: add link - web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_value_holders, bool is_rebuildable) + web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_value_holders, const web::json::array& dependency_paths, const web::json::array& allowed_members_classes, bool is_rebuildable) { using web::json::value_of; return value_of({ { nmos::fields::nc::path, web::json::value_from_elements(role_path)}, + { nmos::fields::nc::dependency_paths, web::json::value_from_elements(dependency_paths)}, + { nmos::fields::nc::allowed_members_classes, web::json::value_from_elements(allowed_members_classes)}, { nmos::fields::nc::values, web::json::value_from_elements(property_value_holders)}, { nmos::fields::nc::is_rebuildable, is_rebuildable} }, true @@ -2222,6 +2224,8 @@ namespace nmos auto fields = value::array(); web::json::push_back(fields, details::make_nc_field_descriptor(U("Object role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Sequence of role paths which are a dependency for this object"), nmos::fields::nc::dependency_paths, U("NcRolePath"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Sequence of class ids allowed as members of the block"), nmos::fields::nc::allowed_members_classes, U("NcClassId"), false, true, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Object properties values"), nmos::fields::nc::values, U("NcPropertyValueHolder"), false, true, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Describes if the object is rebuildable"), nmos::fields::nc::is_rebuildable, U("NcBoolean"), false, false, value::null())); diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 632cd7ab6..9d313da72 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -202,7 +202,7 @@ namespace nmos web::json::value make_nc_property_value_holder(const nc_property_id& property_id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, const web::json::value& property_value); // TODO: add link - web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_value_holders, bool is_rebuildable); + web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_value_holders, const web::json::array& dependency_paths, const web::json::array& allowed_members_classes, bool is_rebuildable); // TODO: add link web::json::value make_nc_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message); diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index b4d3b78d7..8f70071fb 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -304,7 +304,7 @@ namespace nmos const web::json::field_as_bool is_sequence{ U("isSequence") }; const web::json::field_as_bool is_deprecated{ U("isDeprecated") }; const web::json::field_as_bool is_constant{ U("isConstant") }; - const web::json::field_as_string parent_type{ U("parentType") }; + const web::json::field_as_value parent_type{ U("parentType") }; const web::json::field_as_string event_datatype{ U("eventDatatype") }; const web::json::field_as_string result_datatype{ U("resultDatatype") }; const web::json::field_as_array parameters{ U("parameters") }; @@ -345,6 +345,8 @@ namespace nmos const web::json::field_as_string notice_message{ U("noticeMessage") }; const web::json::field_as_array notices{ U("notices") }; const web::json::field_as_integer restore_mode{ U("restoreMode") }; + const web::json::field_as_array dependency_paths{ U("dependencyPaths") }; + const web::json::field_as_array allowed_members_classes{ U("allowedMembersClasses") }; } // NMOS Parameter Registers diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 5bd4b81f1..10b3d7aac 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -92,7 +92,7 @@ BST_TEST_CASE(testIsBlockModified) const nmos::nc_property_id property_id(2, 2); // block members const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(!nmos::is_block_modified(receivers, object_properties_holder)); } @@ -108,7 +108,7 @@ BST_TEST_CASE(testIsBlockModified) const nmos::nc_property_id property_id(2, 2); // block members const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -130,7 +130,7 @@ BST_TEST_CASE(testIsBlockModified) const nmos::nc_property_id property_id(2, 2); // block members const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -152,7 +152,7 @@ BST_TEST_CASE(testIsBlockModified) const nmos::nc_property_id property_id(2, 2); // block members const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -174,7 +174,7 @@ BST_TEST_CASE(testIsBlockModified) const nmos::nc_property_id property_id(2, 2); // block members const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -196,7 +196,7 @@ BST_TEST_CASE(testIsBlockModified) const nmos::nc_property_id property_id(2, 2); // block members const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -218,7 +218,7 @@ BST_TEST_CASE(testIsBlockModified) const nmos::nc_property_id property_id(2, 2); // block members const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); web::json::push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -238,7 +238,7 @@ BST_TEST_CASE(testGetObjectPropertiesHolder) auto property_value_holders = value::array(); const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); } { @@ -246,7 +246,7 @@ BST_TEST_CASE(testGetObjectPropertiesHolder) auto property_value_holders = value::array(); const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); } { @@ -254,7 +254,7 @@ BST_TEST_CASE(testGetObjectPropertiesHolder) auto property_value_holders = value::array(); const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); } @@ -296,7 +296,7 @@ BST_TEST_CASE(testGetChildObjectPropertiesHolders) auto property_value_holders = value::array(); const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); } { @@ -304,7 +304,7 @@ BST_TEST_CASE(testGetChildObjectPropertiesHolders) auto property_value_holders = value::array(); const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); } { @@ -312,7 +312,7 @@ BST_TEST_CASE(testGetChildObjectPropertiesHolders) auto property_value_holders = value::array(); const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); } @@ -475,7 +475,7 @@ BST_TEST_CASE(testApplyBackupDataSet) auto property_value_holders = value::array(); const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); const auto target_role_path = value_of({ U("root"), U("receivers")}); bool recurse = true; @@ -511,7 +511,7 @@ BST_TEST_CASE(testApplyBackupDataSet) // This is a read only property const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -549,7 +549,7 @@ BST_TEST_CASE(testApplyBackupDataSet) // This is a read only property const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -599,7 +599,7 @@ BST_TEST_CASE(testApplyBackupDataSet) push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), true, value("change this value"))); // This is a writable property push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false)); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -646,7 +646,7 @@ BST_TEST_CASE(testApplyBackupDataSet) push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; @@ -680,7 +680,7 @@ BST_TEST_CASE(testApplyBackupDataSet) push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; @@ -714,7 +714,7 @@ BST_TEST_CASE(testApplyBackupDataSet) // This is a read only property push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value"))); //read only push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcString"), false, false)); // error in data type - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -758,7 +758,7 @@ BST_TEST_CASE(testApplyBackupDataSet) auto property_value_holders = value::array(); // This is a read only property push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("wrong_property_name"), U("NcString"), false, value("change this value"))); //read only - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -802,7 +802,7 @@ BST_TEST_CASE(testApplyBackupDataSet) auto property_value_holders = value::array(); // This is a read only property push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("wrong_data_type"), false, value("change this value"))); //read only - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -890,7 +890,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto property_value_holders = value::array(); const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false); push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -917,7 +917,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto property_value_holders = value::array(); const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false); push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -946,7 +946,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) // This is a read only property const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -976,7 +976,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), false)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; From 58c2b9f37b19f2b4ff288552357f139644046eb8 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 19 May 2025 14:50:53 +0100 Subject: [PATCH 179/250] Return inherited fields in structs on datatype descriptor endpoint --- Development/nmos/configuration_api.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 441c1c992..2ef42ef8d 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -312,6 +312,7 @@ namespace nmos if (!class_id.empty()) { + // Hmmm, this feels like it should be a utility function, or we just parameterize the handler to include inherited const auto& control_class = get_control_protocol_class_descriptor(class_id); auto& description = control_class.description; @@ -386,7 +387,7 @@ namespace nmos }); // GET /rolePaths/{rolePath}/properties/{propertyId}/descriptor - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/descriptor/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/descriptor/?"), methods::GET, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto property_id = parameters.at(nmos::patterns::propertyId.name); const auto role_path = parameters.at(nmos::patterns::rolePath.name); @@ -399,13 +400,33 @@ namespace nmos { // find the relevant nc_property_descriptor const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); + const auto& property_type = nmos::fields::nc::type_name(property_descriptor); + auto datatype_descriptor = nc::details::get_datatype_descriptor(value::string(property_type), get_control_protocol_datatype_descriptor); + + if (nmos::nc_datatype_type::Struct == nmos::fields::nc::type(datatype_descriptor)) + { + auto inherited_struct = datatype_descriptor; + + while (!nmos::fields::nc::parent_type(inherited_struct).is_null()) + { + auto parent_type = nmos::fields::nc::parent_type(datatype_descriptor).as_string(); + + inherited_struct = nc::details::get_datatype_descriptor(value::string(parent_type), get_control_protocol_datatype_descriptor); + + for (const auto field : nmos::fields::nc::fields(inherited_struct)) + { + web::json::push_back(datatype_descriptor[nmos::fields::nc::fields], field); + } + } + } + if (property_descriptor.is_null()) { set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); } else { - auto method_result = details::make_nc_method_result({ nmos::nc_method_status::ok }, property_descriptor); + auto method_result = details::make_nc_method_result({ nmos::nc_method_status::ok }, datatype_descriptor); set_reply(res, status_codes::OK, method_result); } } From 671e73453f43b0137e727d508c0c114a3c3b9541 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 28 May 2025 17:23:55 +0100 Subject: [PATCH 180/250] Fix master_gain owner bug --- Development/nmos-cpp-node/node_implementation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index a7fc0681e..6452af402 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1230,7 +1230,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr nmos::nc::push_back(channel_gain, right_gain); // example master-gain - auto master_gain = make_gain_control(++oid, channel_gain_oid, U("master-gain"), U("Master gain"), U("Master gain block"), value::null(), value::null(), 0.0); + auto master_gain = make_gain_control(++oid, stereo_gain_oid, U("master-gain"), U("Master gain"), U("Master gain block"), value::null(), value::null(), 0.0); // add channel-gain and master-gain to stereo-gain nmos::nc::push_back(stereo_gain, channel_gain); nmos::nc::push_back(stereo_gain, master_gain); From 92328e7a2baf8cb5aacd941edcd1af7b495d8283 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 28 May 2025 17:24:37 +0100 Subject: [PATCH 181/250] Fix bulkProperties status code bug --- Development/nmos/configuration_api.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 2ef42ef8d..84631bb0b 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -725,6 +725,8 @@ namespace nmos method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + code = status_codes::OK; + model.notify(); } catch (const nmos::control_protocol_exception& e) From 4c0fab9687ef247c814c1590341e82722ad0e010 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Wed, 28 May 2025 17:24:53 +0100 Subject: [PATCH 182/250] Fix bulkProperties recurse bug --- Development/nmos/configuration_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index f6a72e28d..cc4b37c99 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -189,7 +189,7 @@ namespace nmos const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); - if (nmos::nc::is_block(class_id)) + if (recurse && nmos::nc::is_block(class_id)) { // if rebuildable and the block has changed then callback if (nmos::fields::nc::is_rebuildable(resource.data) && target_object_properties_holders.size() && is_block_modified(resource, *target_object_properties_holders.begin())) From db506d6a980080a4a498f881e5a8f0d2bbf70efa Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 30 May 2025 11:40:20 +0100 Subject: [PATCH 183/250] Improve error handling --- Development/nmos/configuration_api.cpp | 78 +++++++++++++++++++------- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 84631bb0b..29abb7b82 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -204,7 +204,8 @@ namespace nmos else { // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); } return pplx::task_from_result(true); @@ -244,7 +245,8 @@ namespace nmos else { // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); } return pplx::task_from_result(true); @@ -291,7 +293,8 @@ namespace nmos else { // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); } return pplx::task_from_result(true); @@ -348,7 +351,8 @@ namespace nmos else { // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); } return pplx::task_from_result(true); @@ -370,7 +374,9 @@ namespace nmos const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); + // resource not found for the role path + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::property_not_implemented }, U("Not Found; ") + property_id); + set_reply(res, status_codes::NotFound, method_result); } else { @@ -380,7 +386,8 @@ namespace nmos else { // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); } return pplx::task_from_result(true); @@ -422,7 +429,9 @@ namespace nmos if (property_descriptor.is_null()) { - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); + // property not found + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::property_not_implemented }, U("Not Found; ") + property_id); + set_reply(res, status_codes::NotFound, method_result); } else { @@ -433,7 +442,8 @@ namespace nmos else { // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); } return pplx::task_from_result(true); @@ -463,7 +473,8 @@ namespace nmos else { // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); } return pplx::task_from_result(true); @@ -538,7 +549,8 @@ namespace nmos else { // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); } return true; @@ -570,7 +582,9 @@ namespace nmos const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + property_id); + // property not found + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + property_id); + set_reply(res, status_codes::NotFound, method_result); } else { @@ -590,7 +604,8 @@ namespace nmos else { // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); } return true; @@ -637,7 +652,8 @@ namespace nmos else { // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); } return pplx::task_from_result(true); @@ -656,14 +672,14 @@ namespace nmos const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { - // Validate JSON syntax according to the schema - details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_validate_request_schema_uri(version)); - web::http::status_code code{ status_codes::BadRequest }; value method_result; try { + // Validate JSON syntax according to the schema + details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_validate_request_schema_uri(version)); + const auto& arguments = nmos::fields::nc::arguments(body); bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -686,12 +702,22 @@ namespace nmos code = status_codes::BadRequest; } + catch (const web::json::json_exception& e) + { + // JSON validation error + utility::stringstream_t ss; + ss << U("JSON validation error: ") << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + + code = status_codes::BadRequest; + } set_reply(res, code, method_result); } else { // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); } return true; }); @@ -710,14 +736,14 @@ namespace nmos const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { - // Validate JSON syntax according to the schema - details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_set_request_schema_uri(version)); - web::http::status_code code{ status_codes::BadRequest }; value method_result; try { + // Validate JSON syntax according to the schema + details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_set_request_schema_uri(version)); + const auto& arguments = nmos::fields::nc::arguments(body); bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -738,12 +764,22 @@ namespace nmos code = status_codes::BadRequest; } + catch (const web::json::json_exception& e) + { + // JSON validation error + utility::stringstream_t ss; + ss << U("JSON validation error: ") << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + + code = status_codes::BadRequest; + } set_reply(res, code, method_result); } else { // resource not found for the role path - set_error_reply(res, status_codes::NotFound, U("Not Found; ") + role_path); + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); } return true; From 28a7d53288902468400bba5d8e3b39701164a5d4 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 13 Jun 2025 11:18:03 +0100 Subject: [PATCH 184/250] Simplify configuration api callbacks --- .../nmos-cpp-node/node_implementation.cpp | 96 ++++--------------- 1 file changed, 21 insertions(+), 75 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 6452af402..82be0d937 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1208,6 +1208,8 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // example device manager auto device_manager = nmos::make_device_manager(++oid, model.settings); + // making an object rebuildable allows read only properties to be modified by the Configuration API in Rebuild mode + nmos::make_rebuildable(device_manager); // example class manager auto class_manager = nmos::make_class_manager(++oid, control_protocol_state); @@ -1225,6 +1227,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // example left/right gains auto left_gain = make_gain_control(++oid, channel_gain_oid, U("left-gain"), U("Left gain"), U("Left channel gain"), value::null(), value::null(), 0.0); auto right_gain = make_gain_control(++oid, channel_gain_oid, U("right-gain"), U("Right gain"), U("Right channel gain"), value::null(), value::null(), 0.0); + // add left-gain and right-gain to channel gain nmos::nc::push_back(channel_gain, left_gain); nmos::nc::push_back(channel_gain, right_gain); @@ -1262,6 +1265,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const auto receiver_block_oid = ++oid; auto receiver_block = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receiver Monitors"), U("Receiver Monitors")); + // making a block rebuildable allows block members to be added or removed by the Configuration API in Rebuild mode nmos::make_rebuildable(receiver_block); // example receiver-monitor(s) @@ -1753,8 +1757,12 @@ nmos::filter_property_value_holders_handler make_filter_property_value_holders_h const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); - // In this example we are only allowing writable properties to be modified - if (bool(nmos::fields::nc::is_read_only(property_descriptor))) + // In this example we are not allowing "structural" parts of an object to be modified + if (nmos::fields::nc::name(property_descriptor) == nmos::fields::nc::oid.key + || nmos::fields::nc::name(property_descriptor) == nmos::fields::nc::constant_oid.key + || nmos::fields::nc::name(property_descriptor) == nmos::fields::nc::role.key + || nmos::fields::nc::name(property_descriptor) == nmos::fields::nc::class_id.key + || nmos::fields::nc::name(property_descriptor) == nmos::fields::nc::owner.key) { // We need to create a notice for any properties that will not be updated 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("Update of read only properties not supported")); @@ -1769,11 +1777,14 @@ nmos::filter_property_value_holders_handler make_filter_property_value_holders_h }; } +// JRT perhaps we need two call backs - one for deleting a resource, and one for creating a resource + // Example Device Configuration callback for restoring a back-up dataset nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmos::node_model& model, slog::base_gate& gate) { return [&model, &gate](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { + // rebuildable block and child objects are passed to this function for modification auto object_properties_set_validations = web::json::value::array(); nmos::resources& control_protocol_resources = model.control_protocol_resources; @@ -1839,23 +1850,10 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo // get the receiver monitor resource auto found = nmos::find_resource(control_protocol_resources, utility::conversions::details::to_string_t(nmos::fields::nc::oid(reference_member))); - const auto& touchpoint_resource = nmos::nc::find_touchpoint_resource(node_resources, *found); - - if (touchpoint_resource != control_protocol_resources.end()) + if (control_protocol_resources.end() != found) { - const auto& id = nmos::fields::id(touchpoint_resource->data); - auto erase_count = erase_resource(node_resources, id); - - if (erase_count == 0) - { - auto status_message = U("Unable to erase node resource ") + id; - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); - - continue; - } const auto oid = nmos::fields::nc::oid(found->data); - erase_count = nmos::nc::erase_resource(control_protocol_resources, found->id); + auto erase_count = nmos::nc::erase_resource(control_protocol_resources, found->id); if (erase_count > 0) { members_to_remove.push_back(oid); @@ -1903,29 +1901,7 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo { // can't find this oid in existing members, so member has been added // Add this resource - // Get example resource from the exising members to get node_id, device_id - if (reference_members.size() == 0) - { - auto status_message = U("Cannot duplicate resources when none exist"); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); - - continue; - } - const auto& example_monitor = *reference_members.begin(); - const auto& found = nmos::find_resource(control_protocol_resources, utility::conversions::details::to_string_t(nmos::fields::nc::oid(example_monitor))); - const auto& touchpoint_resource = nmos::nc::find_touchpoint_resource(node_resources, *found); - if (touchpoint_resource == control_protocol_resources.end()) - { - auto status_message = U("Cannot duplicate resources when none exist"); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); - - continue; - } - const auto& device_id = nmos::fields::device_id(touchpoint_resource->data); - - // Find the object_properties_holder that describes the new receiver monitor + // Find the object_properties_holder that describes the receiver monitor const auto& filtered_child_object_properties_holders = boost::copy_range>(object_properties_holders | boost::adaptors::filtered([&child_role_path](const web::json::value& object_properties_holder) { @@ -1944,8 +1920,10 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo const auto& child_object_properties_holder = *filtered_child_object_properties_holders.begin(); - const auto& oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_oid_property_id); + // JRT TODO: unsafe to take oid from backup dataset - need to create a new OID with no clash with existing OIDs so need a utility function to get "next" OID + // create utility function to get next oid - perhaps have a state in the control_protocol_state for a monotonically increasing oid + const auto& oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_oid_property_id); if (oid_property_holder == web::json::value::null()) { auto status_message = U("Cannot find OID object property value holder"); @@ -1965,7 +1943,6 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo continue; } - const auto& oid2 = nmos::fields::nc::value(oid_property_holder); const auto& touchpoints = nmos::fields::nc::value(touchpoint_property_holder); @@ -1979,35 +1956,6 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo } const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); - // Make resources - const auto host_interfaces = nmos::get_host_interfaces(model.settings); - const auto& host_address = nmos::fields::host_address(model.settings); - // the interface corresponding to the host address is used for the example node's WebSocket senders and receivers - const auto host_interface_ = impl::find_interface(host_interfaces, host_address); - if (host_interfaces.end() == host_interface_) - { - slog::log(gate, SLOG_FLF) << "No network interface corresponding to host_address?"; - throw node_implementation_init_exception(); - } - - const auto& primary_address = model.settings.has_field(nmos::fields::host_addresses) ? web::json::front(nmos::fields::host_addresses(model.settings)).as_string() : host_address; - const auto& secondary_address = model.settings.has_field(nmos::fields::host_addresses) ? web::json::back(nmos::fields::host_addresses(model.settings)).as_string() : host_address; - const auto primary_interface_ = impl::find_interface(host_interfaces, primary_address); - const auto secondary_interface_ = impl::find_interface(host_interfaces, secondary_address); - if (host_interfaces.end() == primary_interface_ || host_interfaces.end() == secondary_interface_) - { - slog::log(gate, SLOG_FLF) << "No network interface corresponding to one of the host_addresses?"; - throw node_implementation_init_exception(); - } - const auto& primary_interface = *primary_interface_; - const auto& secondary_interface = *secondary_interface_; - const auto smpte2022_7 = impl::fields::smpte2022_7(model.settings); - const auto interface_names = smpte2022_7 - ? std::vector{ primary_interface.name, secondary_interface.name } - : std::vector{ primary_interface.name }; - - auto receiver = nmos::make_receiver(touchpoint_uuid.as_string(), device_id, nmos::transports::rtp, interface_names, model.settings); - const auto& owner_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_owner_property_id); if (owner_property_holder == web::json::value::null()) { @@ -2030,15 +1978,13 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo const auto& owner = nmos::fields::nc::value(owner_property_holder).as_integer(); const auto& role = nmos::fields::nc::value(role_property_holder).as_string(); + const auto& oid2 = nmos::fields::nc::value(oid_property_holder); auto receiver_monitor = nmos::make_receiver_monitor(oid2.as_integer(), true, owner, role, U(""), U(""), web::json::value_of({{nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})}})); + nmos::nc::insert_resource(control_protocol_resources, std::move(receiver_monitor)); auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U(""), role, oid2.as_integer(), true, nmos::nc_receiver_monitor_class_id, U(""), owner); - members_to_add.push_back(block_member_descriptor); - // insert resources - insert_resource(node_resources, std::move(receiver)); - nmos::nc::insert_resource(control_protocol_resources, std::move(receiver_monitor)); auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); web::json::push_back(object_properties_set_validations, object_properties_set_validation); From f804d312adfdc022fe1f08a0c2a5cd0c806b94a0 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 13 Jun 2025 16:58:03 +0100 Subject: [PATCH 185/250] Factor out adding and removing block members --- .../nmos-cpp-node/node_implementation.cpp | 174 +++++++++++------- 1 file changed, 109 insertions(+), 65 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 82be0d937..355f73e77 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1740,7 +1740,9 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control }; } -// Example Device Configuration callback for validating a back-up dataset +// Example Device Configuration callback called when a rebuildable object is modified in Rebuild mode. +// An array of property values is passed in, and an array of property values that can be modified is returned +// For each property value that can't be returned a property restore notice must be created nmos::filter_property_value_holders_handler make_filter_property_value_holders_handler(nmos::resources& resources, slog::base_gate& gate) { return [&resources, &gate](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) @@ -1778,6 +1780,76 @@ nmos::filter_property_value_holders_handler make_filter_property_value_holders_h } // JRT perhaps we need two call backs - one for deleting a resource, and one for creating a resource +bool remove_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate, const nmos::nc_oid reference_oid) +{ + nmos::resources& control_protocol_resources = model.control_protocol_resources; + + // get the receiver monitor resource + auto found = nmos::find_resource(control_protocol_resources, utility::conversions::details::to_string_t(reference_oid)); + + if (control_protocol_resources.end() != found) + { + const auto oid = nmos::fields::nc::oid(found->data); + auto erase_count = nmos::nc::erase_resource(control_protocol_resources, found->id); + if (erase_count > 0) + { + return true; + } + } + return false; +} + +web::json::value add_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate, const web::json::value& object_properties_holder) +{ + nmos::resources& control_protocol_resources = model.control_protocol_resources; + + const auto& child_role_path = nmos::fields::nc::path(object_properties_holder); + + const auto& oid_property_holder = nmos::get_property_value_holder(object_properties_holder, nmos::nc_object_oid_property_id); + if (oid_property_holder == web::json::value::null()) + { + auto status_message = U("Cannot find OID object property value holder"); + return nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::failed, status_message); + } + + const auto& touchpoint_property_holder = nmos::get_property_value_holder(object_properties_holder, nmos::nc_property_id(1, 7)); + if (touchpoint_property_holder == web::json::value::null()) + { + auto status_message = U("Cannot find touchpoint object property value holder"); + return nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::failed, status_message); + } + + const auto& touchpoints = nmos::fields::nc::value(touchpoint_property_holder); + if (touchpoints.size() != 1) + { + auto status_message = U("Either zero or more than one touchpoint found (ambiguous)."); + return nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::failed, status_message); + } + const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); + + const auto& owner_property_holder = nmos::get_property_value_holder(object_properties_holder, nmos::nc_object_owner_property_id); + if (owner_property_holder == web::json::value::null()) + { + auto status_message = U("Cannot find owner property value holder."); + return nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::failed, status_message); + } + + const auto& role_property_holder = nmos::get_property_value_holder(object_properties_holder, nmos::nc_object_role_property_id); + if (role_property_holder == web::json::value::null()) + { + auto status_message = U("Cannot find role property value holder."); + return nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::failed, status_message); + } + + const auto& owner = nmos::fields::nc::value(owner_property_holder).as_integer(); + const auto& role = nmos::fields::nc::value(role_property_holder).as_string(); + const auto& oid2 = nmos::fields::nc::value(oid_property_holder); + + auto receiver_monitor = nmos::make_receiver_monitor(oid2.as_integer(), true, owner, role, U(""), U(""), web::json::value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})} })); + nmos::nc::insert_resource(control_protocol_resources, std::move(receiver_monitor)); + + return nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::ok); +} // Example Device Configuration callback for restoring a back-up dataset nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmos::node_model& model, slog::base_gate& gate) @@ -1848,42 +1920,35 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo { // can't find this oid in restore dataset, so member has been removed // get the receiver monitor resource - auto found = nmos::find_resource(control_protocol_resources, utility::conversions::details::to_string_t(nmos::fields::nc::oid(reference_member))); - - if (control_protocol_resources.end() != found) + bool success = remove_device_model_object_handler(model, gate, nmos::fields::nc::oid(reference_member)); + if (success) { - const auto oid = nmos::fields::nc::oid(found->data); - auto erase_count = nmos::nc::erase_resource(control_protocol_resources, found->id); - if (erase_count > 0) - { - members_to_remove.push_back(oid); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); - } + members_to_remove.push_back(nmos::fields::nc::oid(reference_member)); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); } } else { - const auto restore_member = *filtered_members.begin(); - // We ignore the description and user label as these are non-normative - if (nmos::fields::nc::role(reference_member) != nmos::fields::nc::role(restore_member) - || nmos::fields::nc::constant_oid(reference_member) != nmos::fields::nc::constant_oid(restore_member) - || nmos::fields::nc::class_id(reference_member) != nmos::fields::nc::class_id(restore_member) - || nmos::fields::nc::owner(reference_member) != nmos::fields::nc::owner(restore_member)) - { - // Modify existing resource - // in this example we will ignore/reject changes to the role, constant_oid, class_id or owner - // Do nothing, return warning - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); - continue; - } - else - { + // JRT TODO: check to see if the object is rebuildable and then modify according to the filter rules + + //const auto restore_member = *filtered_members.begin(); + //if (bool(nmos::fields::nc::is_rebuildable(restore_member))) + //{ + // // Modify existing resource + // // in this example we will ignore/reject changes to the role, constant_oid, class_id or owner + // // Do nothing, return warning + // // call back to the filter_property_value_holders_handler and then modify the value + // auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); + // web::json::push_back(object_properties_set_validations, object_properties_set_validation); + // continue; + //} + //else + //{ // Do nothing auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); web::json::push_back(object_properties_set_validations, object_properties_set_validation); - } + //} } } for (const auto& restore_member : restore_members.as_array()) @@ -1922,7 +1987,6 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo // JRT TODO: unsafe to take oid from backup dataset - need to create a new OID with no clash with existing OIDs so need a utility function to get "next" OID // create utility function to get next oid - perhaps have a state in the control_protocol_state for a monotonically increasing oid - const auto& oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_oid_property_id); if (oid_property_holder == web::json::value::null()) { @@ -1932,30 +1996,15 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo continue; } - - const auto& touchpoint_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_property_id(1, 7)); - - if (touchpoint_property_holder == web::json::value::null()) - { - auto status_message = U("Cannot find touchpoint object property value holder"); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); - - continue; - } - - const auto& touchpoints = nmos::fields::nc::value(touchpoint_property_holder); - - if (touchpoints.size() != 1) + const auto& role_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_role_property_id); + if (role_property_holder == web::json::value::null()) { - auto status_message = U("Either zero or more than one touchpoint found (ambiguous)."); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + auto status_message = U("Cannot find role property value holder."); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); web::json::push_back(object_properties_set_validations, object_properties_set_validation); continue; } - const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); - const auto& owner_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_owner_property_id); if (owner_property_holder == web::json::value::null()) { @@ -1966,27 +2015,22 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo continue; } - const auto& role_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_role_property_id); - if (role_property_holder == web::json::value::null()) - { - auto status_message = U("Cannot find role property value holder."); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); - - continue; - } - - const auto& owner = nmos::fields::nc::value(owner_property_holder).as_integer(); + //const auto& owner = nmos::fields::nc::value(owner_property_holder).as_integer(); const auto& role = nmos::fields::nc::value(role_property_holder).as_string(); const auto& oid2 = nmos::fields::nc::value(oid_property_holder); + const auto& owner = nmos::fields::nc::value(owner_property_holder); - auto receiver_monitor = nmos::make_receiver_monitor(oid2.as_integer(), true, owner, role, U(""), U(""), web::json::value_of({{nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})}})); - nmos::nc::insert_resource(control_protocol_resources, std::move(receiver_monitor)); + auto object_properties_set_validation = add_device_model_object_handler(model, gate, child_object_properties_holder); - auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U(""), role, oid2.as_integer(), true, nmos::nc_receiver_monitor_class_id, U(""), owner); - members_to_add.push_back(block_member_descriptor); + if (nmos::fields::nc::status(object_properties_set_validation) == nmos::nc_restore_validation_status::ok) + { + auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U(""), role, oid2.as_integer(), true, nmos::nc_receiver_monitor_class_id, U(""), owner.as_integer()); + members_to_add.push_back(block_member_descriptor); + + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + } - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); web::json::push_back(object_properties_set_validations, object_properties_set_validation); } } From 3b346e12bbf853f47384121811b4dba1204e9db6 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 16 Jun 2025 16:23:17 +0100 Subject: [PATCH 186/250] Ensure block members correctly created when adding device model objects --- .../nmos-cpp-node/node_implementation.cpp | 158 ++++++++++-------- 1 file changed, 92 insertions(+), 66 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 355f73e77..19988e360 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1780,7 +1780,7 @@ nmos::filter_property_value_holders_handler make_filter_property_value_holders_h } // JRT perhaps we need two call backs - one for deleting a resource, and one for creating a resource -bool remove_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate, const nmos::nc_oid reference_oid) +bool remove_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate, const nmos::nc_oid reference_oid, bool validate) { nmos::resources& control_protocol_resources = model.control_protocol_resources; @@ -1789,66 +1789,65 @@ bool remove_device_model_object_handler(nmos::node_model& model, slog::base_gate if (control_protocol_resources.end() != found) { - const auto oid = nmos::fields::nc::oid(found->data); - auto erase_count = nmos::nc::erase_resource(control_protocol_resources, found->id); - if (erase_count > 0) + if (!validate) { - return true; + const auto oid = nmos::fields::nc::oid(found->data); + auto erase_count = nmos::nc::erase_resource(control_protocol_resources, found->id); + if (erase_count > 0) + { + return true; + } } } return false; } -web::json::value add_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate, const web::json::value& object_properties_holder) +web::json::value add_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate, const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate) { + // JRT TODO: Add some boiler plate to add notices for unused property value holders nmos::resources& control_protocol_resources = model.control_protocol_resources; - const auto& child_role_path = nmos::fields::nc::path(object_properties_holder); + const auto& role_path = nmos::fields::nc::path(object_properties_holder); + const auto& allowed_member_classes = nmos::fields::nc::allowed_members_classes(object_properties_holder); - const auto& oid_property_holder = nmos::get_property_value_holder(object_properties_holder, nmos::nc_object_oid_property_id); - if (oid_property_holder == web::json::value::null()) + if (allowed_member_classes.size() > 0) { - auto status_message = U("Cannot find OID object property value holder"); - return nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::failed, status_message); - } + // If allowed member classes array populated, ensure that the receiver monitor class is present + const auto& filtered_classes = boost::copy_range>(allowed_member_classes + | boost::adaptors::filtered([&](const web::json::value& member) + { + return nmos::details::parse_nc_class_id(member.as_array()) == nmos::nc_receiver_monitor_class_id; + }) + ); - const auto& touchpoint_property_holder = nmos::get_property_value_holder(object_properties_holder, nmos::nc_property_id(1, 7)); + // If receiver monitor class not allowed then return with error + if (filtered_classes.size() == 0) + { + auto status_message = U("Device model error: attempting to add unexpected class"); + return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::device_error, status_message); + } + } + const auto& touchpoint_property_holder = nmos::get_property_value_holder(object_properties_holder, nmos::nc_object_touchpoints_property_id); if (touchpoint_property_holder == web::json::value::null()) { auto status_message = U("Cannot find touchpoint object property value holder"); - return nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::failed, status_message); + return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::failed, status_message); } const auto& touchpoints = nmos::fields::nc::value(touchpoint_property_holder); if (touchpoints.size() != 1) { auto status_message = U("Either zero or more than one touchpoint found (ambiguous)."); - return nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::failed, status_message); + return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::failed, status_message); } - const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); - - const auto& owner_property_holder = nmos::get_property_value_holder(object_properties_holder, nmos::nc_object_owner_property_id); - if (owner_property_holder == web::json::value::null()) + if (!validate) { - auto status_message = U("Cannot find owner property value holder."); - return nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::failed, status_message); - } + const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); - const auto& role_property_holder = nmos::get_property_value_holder(object_properties_holder, nmos::nc_object_role_property_id); - if (role_property_holder == web::json::value::null()) - { - auto status_message = U("Cannot find role property value holder."); - return nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::failed, status_message); + auto receiver_monitor = nmos::make_receiver_monitor(oid, true, owner, role, user_label, U(""), web::json::value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})} })); + nmos::nc::insert_resource(control_protocol_resources, std::move(receiver_monitor)); } - - const auto& owner = nmos::fields::nc::value(owner_property_holder).as_integer(); - const auto& role = nmos::fields::nc::value(role_property_holder).as_string(); - const auto& oid2 = nmos::fields::nc::value(oid_property_holder); - - auto receiver_monitor = nmos::make_receiver_monitor(oid2.as_integer(), true, owner, role, U(""), U(""), web::json::value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})} })); - nmos::nc::insert_resource(control_protocol_resources, std::move(receiver_monitor)); - - return nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::ok); + return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::ok); } // Example Device Configuration callback for restoring a back-up dataset @@ -1920,12 +1919,15 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo { // can't find this oid in restore dataset, so member has been removed // get the receiver monitor resource - bool success = remove_device_model_object_handler(model, gate, nmos::fields::nc::oid(reference_member)); + bool success = remove_device_model_object_handler(model, gate, nmos::fields::nc::oid(reference_member), validate); if (success) { - members_to_remove.push_back(nmos::fields::nc::oid(reference_member)); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); + if (!validate) + { + members_to_remove.push_back(nmos::fields::nc::oid(reference_member)); + } + //auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); + //web::json::push_back(object_properties_set_validations, object_properties_set_validation); } } else @@ -1946,11 +1948,14 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo //else //{ // Do nothing - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); //} } } + // If there are any data problems they should be reported as warning/error notices + auto block_notices = web::json::value::array(); + for (const auto& restore_member : restore_members.as_array()) { auto child_role_path = web::json::value_from_elements(target_role_path); @@ -1974,6 +1979,7 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo }) ); + // JRT TODO: perhaps the resource not existing isn't an obsticle to the new object being created? if (filtered_child_object_properties_holders.size() != 1) { auto status_message = U("Cannot find NcObjectPropertiesHolder for new resource"); @@ -1987,6 +1993,17 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo // JRT TODO: unsafe to take oid from backup dataset - need to create a new OID with no clash with existing OIDs so need a utility function to get "next" OID // create utility function to get next oid - perhaps have a state in the control_protocol_state for a monotonically increasing oid + // Get member descriptor properties + const auto& block_member_description = nmos::fields::nc::description(restore_member); + const auto& block_member_role = nmos::fields::nc::role(restore_member); + const auto& block_member_oid = nmos::fields::nc::oid(restore_member); + const auto& block_member_owner = nmos::fields::nc::owner(restore_member); + const auto& block_member_constant_oid = nmos::fields::nc::constant_oid(restore_member); + const auto& block_member_user_label = nmos::fields::nc::user_label(restore_member); + + const auto block_member_notices = web::json::value::array(); + + // JRT TODO: validate the block member values against the child object definitions const auto& oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_oid_property_id); if (oid_property_holder == web::json::value::null()) { @@ -1996,44 +2013,53 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo continue; } + // The values in the block member Object Property Holder will take precidence over the block member descriptor values + // If the block member values are inconsistant then warn - description and user label are independant const auto& role_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_role_property_id); - if (role_property_holder == web::json::value::null()) - { - auto status_message = U("Cannot find role property value holder."); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); + const auto role = role_property_holder == web::json::value::null() ? block_member_role.c_str() : nmos::fields::nc::value(role_property_holder).as_string(); - continue; + // JRT TODO: add context to error messages i.e. indicate which object has warnings + if (role_property_holder != web::json::value::null() && role != block_member_role.c_str()) + { + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Role value in block member inconsistent with value in property holder. Property holder value takes precidence.")); + web::json::push_back(block_notices, notice); } + // JRT TODO: owner is objectively the oid of the block - check here for consistency with the block and warn const auto& owner_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_owner_property_id); - if (owner_property_holder == web::json::value::null()) + const auto& owner = owner_property_holder == web::json::value::null() ? block_member_owner : nmos::fields::nc::value(owner_property_holder); + + if (owner_property_holder != web::json::value::null() && owner != block_member_owner) { - auto status_message = U("Cannot find owner property value holder."); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Owner value in block member inconsistent with value in property holder. Property holder value takes precidence.")); + web::json::push_back(block_notices, notice); + } + const auto& constant_oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_constant_oid_property_id); + const auto& constant_oid = constant_oid_property_holder == web::json::value::null() ? block_member_constant_oid : nmos::fields::nc::value(constant_oid_property_holder); - continue; + if (constant_oid_property_holder != web::json::value::null() && constant_oid.as_bool() != block_member_constant_oid) + { + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Constant OID value in block member inconsistent with value in property holder. Property holder value takes precidence.")); + web::json::push_back(block_notices, notice); } - //const auto& owner = nmos::fields::nc::value(owner_property_holder).as_integer(); - const auto& role = nmos::fields::nc::value(role_property_holder).as_string(); - const auto& oid2 = nmos::fields::nc::value(oid_property_holder); - const auto& owner = nmos::fields::nc::value(owner_property_holder); + const auto& user_label_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_user_label_property_id); + const auto user_label = (owner_property_holder == web::json::value::null()) ? U("") : nmos::fields::nc::value(user_label_property_holder).as_string(); + + const auto& oid2 = nmos::fields::nc::value(oid_property_holder).as_integer(); - auto object_properties_set_validation = add_device_model_object_handler(model, gate, child_object_properties_holder); + auto object_properties_set_validation = add_device_model_object_handler(model, gate, child_object_properties_holder, oid2, owner.as_integer(), role, user_label, validate); - if (nmos::fields::nc::status(object_properties_set_validation) == nmos::nc_restore_validation_status::ok) + if (nmos::fields::nc::status(object_properties_set_validation) == nmos::nc_restore_validation_status::ok && !validate) { - auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U(""), role, oid2.as_integer(), true, nmos::nc_receiver_monitor_class_id, U(""), owner.as_integer()); + auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(block_member_description, role, oid2, constant_oid.as_bool(), nmos::nc_receiver_monitor_class_id, block_member_user_label, owner.as_integer()); members_to_add.push_back(block_member_descriptor); - - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); } web::json::push_back(object_properties_set_validations, object_properties_set_validation); } } + auto block_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, block_notices.as_array()); + web::json::push_back(object_properties_set_validations, block_set_validation); // Update the members of the receivers block if (members_to_remove.size() > 0 || members_to_add.size() > 0) @@ -2059,10 +2085,10 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo } nmos::nc::modify_resource(control_protocol_resources, resource.id, [&](nmos::resource& resource) - { - resource.data[nmos::fields::nc::members] = modified_members; + { + resource.data[nmos::fields::nc::members] = modified_members; - }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { { nmos::nc_block_members_property_id, nmos::nc_property_change_type::type::value_changed, modified_members } })); + }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { { nmos::nc_block_members_property_id, nmos::nc_property_change_type::type::value_changed, modified_members } })); } return object_properties_set_validations; From 219e8d1ddc5a4651ac9179c5396ab5d31434aa03 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 16 Jun 2025 17:08:51 +0100 Subject: [PATCH 187/250] Validate owner using block oid against block member and property value holder --- .../nmos-cpp-node/node_implementation.cpp | 55 ++++++++++--------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 19988e360..180fdf378 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1840,7 +1840,7 @@ web::json::value add_device_model_object_handler(nmos::node_model& model, slog:: auto status_message = U("Either zero or more than one touchpoint found (ambiguous)."); return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::failed, status_message); } - if (!validate) + if (!validate) // If validate is true then don't add object to device model, just indicate whether it's possible given the data supplied { const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); @@ -1886,11 +1886,11 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo return object_properties_set_validations; } - const auto& block_members_properties_holder = nmos::get_property_value_holder(*filtered_holders.begin(), nmos::nc_property_id(2, 2)); + const auto& block_members_properties_holder = nmos::get_property_value_holder(*filtered_holders.begin(), nmos::nc_block_members_property_id); if (block_members_properties_holder.is_null()) { - auto status_message = U("No NcBlockMembersPropertiesHolder found"); + auto status_message = U("No block members properties holder found"); auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); web::json::push_back(object_properties_set_validations, object_properties_set_validation); @@ -1900,6 +1900,8 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo const auto& restore_members = nmos::fields::nc::value(block_members_properties_holder); const auto& reference_members = nmos::fields::nc::members(resource.data); + const auto& block_oid = nmos::fields::nc::oid(resource.data); + std::vector members_to_remove; std::vector members_to_add; @@ -1926,8 +1928,6 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo { members_to_remove.push_back(nmos::fields::nc::oid(reference_member)); } - //auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); - //web::json::push_back(object_properties_set_validations, object_properties_set_validation); } } else @@ -1969,6 +1969,7 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo ); if (filtered_members.size() != 1) { + // JRT TODO: make this decision based on role, not OID // can't find this oid in existing members, so member has been added // Add this resource // Find the object_properties_holder that describes the receiver monitor @@ -1979,7 +1980,6 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo }) ); - // JRT TODO: perhaps the resource not existing isn't an obsticle to the new object being created? if (filtered_child_object_properties_holders.size() != 1) { auto status_message = U("Cannot find NcObjectPropertiesHolder for new resource"); @@ -1993,17 +1993,18 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo // JRT TODO: unsafe to take oid from backup dataset - need to create a new OID with no clash with existing OIDs so need a utility function to get "next" OID // create utility function to get next oid - perhaps have a state in the control_protocol_state for a monotonically increasing oid + // + // // Get member descriptor properties + auto role = nmos::fields::nc::role(restore_member); + auto oid = nmos::fields::nc::oid(restore_member); + auto owner = nmos::fields::nc::owner(restore_member); + auto constant_oid = nmos::fields::nc::constant_oid(restore_member); const auto& block_member_description = nmos::fields::nc::description(restore_member); - const auto& block_member_role = nmos::fields::nc::role(restore_member); - const auto& block_member_oid = nmos::fields::nc::oid(restore_member); - const auto& block_member_owner = nmos::fields::nc::owner(restore_member); - const auto& block_member_constant_oid = nmos::fields::nc::constant_oid(restore_member); const auto& block_member_user_label = nmos::fields::nc::user_label(restore_member); - const auto block_member_notices = web::json::value::array(); + auto block_member_notices = web::json::value::array(); - // JRT TODO: validate the block member values against the child object definitions const auto& oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_oid_property_id); if (oid_property_holder == web::json::value::null()) { @@ -2016,42 +2017,44 @@ nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmo // The values in the block member Object Property Holder will take precidence over the block member descriptor values // If the block member values are inconsistant then warn - description and user label are independant const auto& role_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_role_property_id); - const auto role = role_property_holder == web::json::value::null() ? block_member_role.c_str() : nmos::fields::nc::value(role_property_holder).as_string(); + role = role_property_holder == web::json::value::null() ? role : nmos::fields::nc::value(role_property_holder).as_string(); // JRT TODO: add context to error messages i.e. indicate which object has warnings - if (role_property_holder != web::json::value::null() && role != block_member_role.c_str()) + if (role_property_holder != web::json::value::null() && role != nmos::fields::nc::value(role_property_holder).as_string()) { const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Role value in block member inconsistent with value in property holder. Property holder value takes precidence.")); web::json::push_back(block_notices, notice); } - // JRT TODO: owner is objectively the oid of the block - check here for consistency with the block and warn - const auto& owner_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_owner_property_id); - const auto& owner = owner_property_holder == web::json::value::null() ? block_member_owner : nmos::fields::nc::value(owner_property_holder); - - if (owner_property_holder != web::json::value::null() && owner != block_member_owner) + if (owner != block_oid) { - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Owner value in block member inconsistent with value in property holder. Property holder value takes precidence.")); + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Owner value in block member inconsistent with oid of Block. Block oid takes precidence.")); web::json::push_back(block_notices, notice); } + const auto& owner_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_owner_property_id); + if (owner_property_holder != web::json::value::null() && block_oid != nmos::fields::nc::value(owner_property_holder).as_integer()) + { + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Owner value in block property holder inconsistent with oid of Block. Block oid takes precidence.")); + web::json::push_back(block_member_notices, notice); + } const auto& constant_oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_constant_oid_property_id); - const auto& constant_oid = constant_oid_property_holder == web::json::value::null() ? block_member_constant_oid : nmos::fields::nc::value(constant_oid_property_holder); + constant_oid = constant_oid_property_holder == web::json::value::null() ? constant_oid : nmos::fields::nc::value(constant_oid_property_holder).as_bool(); - if (constant_oid_property_holder != web::json::value::null() && constant_oid.as_bool() != block_member_constant_oid) + if (constant_oid_property_holder != web::json::value::null() && constant_oid != nmos::fields::nc::value(constant_oid_property_holder).as_bool()) { const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Constant OID value in block member inconsistent with value in property holder. Property holder value takes precidence.")); web::json::push_back(block_notices, notice); } const auto& user_label_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_user_label_property_id); - const auto user_label = (owner_property_holder == web::json::value::null()) ? U("") : nmos::fields::nc::value(user_label_property_holder).as_string(); - + const auto user_label = (user_label_property_holder == web::json::value::null()) ? U("") : nmos::fields::nc::value(user_label_property_holder).as_string(); const auto& oid2 = nmos::fields::nc::value(oid_property_holder).as_integer(); - auto object_properties_set_validation = add_device_model_object_handler(model, gate, child_object_properties_holder, oid2, owner.as_integer(), role, user_label, validate); + auto object_properties_set_validation = add_device_model_object_handler(model, gate, child_object_properties_holder, oid2, owner, role, user_label, validate); + // JRT TODO: append block_member_notices to the validation if (nmos::fields::nc::status(object_properties_set_validation) == nmos::nc_restore_validation_status::ok && !validate) { - auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(block_member_description, role, oid2, constant_oid.as_bool(), nmos::nc_receiver_monitor_class_id, block_member_user_label, owner.as_integer()); + auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(block_member_description, role, oid2, constant_oid, nmos::nc_receiver_monitor_class_id, block_member_user_label, owner); members_to_add.push_back(block_member_descriptor); } From 391256254d718e728700c3a181d97e52b4d623f6 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Thu, 19 Jun 2025 12:04:45 +0100 Subject: [PATCH 188/250] Moved modification of rebuildable block functions into nmos-cpp. Created callbacks for adding and removing device model objects --- Development/nmos-cpp-node/main.cpp | 2 +- .../nmos-cpp-node/node_implementation.cpp | 335 +------ Development/nmos/configuration_api.cpp | 940 +++++++++--------- Development/nmos/configuration_api.h | 2 +- Development/nmos/configuration_handlers.h | 10 + Development/nmos/configuration_methods.cpp | 9 +- Development/nmos/configuration_methods.h | 4 +- Development/nmos/configuration_utils.cpp | 250 ++++- Development/nmos/configuration_utils.h | 2 +- Development/nmos/control_protocol_state.cpp | 22 +- Development/nmos/control_protocol_state.h | 2 +- Development/nmos/node_server.cpp | 2 +- Development/nmos/node_server.h | 11 +- .../nmos/test/configuration_utils_test.cpp | 420 +++++++- 14 files changed, 1168 insertions(+), 843 deletions(-) diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index 9e710aa15..443e14c21 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -138,7 +138,7 @@ int main(int argc, char* argv[]) .on_request_authorization_code(nmos::experimental::make_request_authorization_code_handler(gate)); // may be omitted, only required for OAuth client which is using the Authorization Code Flow to obtain the access token } - nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.filter_property_value_holders, node_implementation.modify_rebuildable_block); + nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.filter_property_value_holders, node_implementation.remove_device_model_object, node_implementation.add_device_model_object); if (0 <= nmos::fields::control_protocol_ws_port(node_model.settings)) { node_implementation diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 180fdf378..1949732ca 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1779,18 +1779,21 @@ nmos::filter_property_value_holders_handler make_filter_property_value_holders_h }; } -// JRT perhaps we need two call backs - one for deleting a resource, and one for creating a resource -bool remove_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate, const nmos::nc_oid reference_oid, bool validate) +nmos::remove_device_model_object_handler make_remove_device_model_handler(nmos::node_model& model, slog::base_gate& gate) { - nmos::resources& control_protocol_resources = model.control_protocol_resources; + return [&model, &gate](const nmos::nc_oid reference_oid, bool validate) + { + nmos::resources& control_protocol_resources = model.control_protocol_resources; - // get the receiver monitor resource - auto found = nmos::find_resource(control_protocol_resources, utility::conversions::details::to_string_t(reference_oid)); + // get the receiver monitor resource + auto found = nmos::find_resource(control_protocol_resources, utility::conversions::details::to_string_t(reference_oid)); - if (control_protocol_resources.end() != found) - { - if (!validate) + if (control_protocol_resources.end() != found) { + if (validate) // If validate is true then delete the object, just indicate whether it's possible given the data supplied + { + return true; + } const auto oid = nmos::fields::nc::oid(found->data); auto erase_count = nmos::nc::erase_resource(control_protocol_resources, found->id); if (erase_count > 0) @@ -1798,303 +1801,62 @@ bool remove_device_model_object_handler(nmos::node_model& model, slog::base_gate return true; } } - } - return false; -} - -web::json::value add_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate, const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate) -{ - // JRT TODO: Add some boiler plate to add notices for unused property value holders - nmos::resources& control_protocol_resources = model.control_protocol_resources; - - const auto& role_path = nmos::fields::nc::path(object_properties_holder); - const auto& allowed_member_classes = nmos::fields::nc::allowed_members_classes(object_properties_holder); - - if (allowed_member_classes.size() > 0) - { - // If allowed member classes array populated, ensure that the receiver monitor class is present - const auto& filtered_classes = boost::copy_range>(allowed_member_classes - | boost::adaptors::filtered([&](const web::json::value& member) - { - return nmos::details::parse_nc_class_id(member.as_array()) == nmos::nc_receiver_monitor_class_id; - }) - ); - - // If receiver monitor class not allowed then return with error - if (filtered_classes.size() == 0) - { - auto status_message = U("Device model error: attempting to add unexpected class"); - return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::device_error, status_message); - } - } - const auto& touchpoint_property_holder = nmos::get_property_value_holder(object_properties_holder, nmos::nc_object_touchpoints_property_id); - if (touchpoint_property_holder == web::json::value::null()) - { - auto status_message = U("Cannot find touchpoint object property value holder"); - return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::failed, status_message); - } - - const auto& touchpoints = nmos::fields::nc::value(touchpoint_property_holder); - if (touchpoints.size() != 1) - { - auto status_message = U("Either zero or more than one touchpoint found (ambiguous)."); - return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::failed, status_message); - } - if (!validate) // If validate is true then don't add object to device model, just indicate whether it's possible given the data supplied - { - const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); - - auto receiver_monitor = nmos::make_receiver_monitor(oid, true, owner, role, user_label, U(""), web::json::value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})} })); - nmos::nc::insert_resource(control_protocol_resources, std::move(receiver_monitor)); - } - return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::ok); + return false; + }; } -// Example Device Configuration callback for restoring a back-up dataset -nmos::modify_rebuildable_block_handler make_modify_rebuildable_block_handler(nmos::node_model& model, slog::base_gate& gate) +nmos::add_device_model_object_handler make_add_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate) { - return [&model, &gate](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + return[&model, &gate](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate) { - // rebuildable block and child objects are passed to this function for modification - auto object_properties_set_validations = web::json::value::array(); - + // This example callback shows how to add a receiver monitor resource to the device model + // The receivers block that contains the monitors must be rebuildable + // To add a monitor, restore a backup dataset including an additional monitor in the members property of the receivers block, in Rebuild mode + // Also include an object properties holder for the new monitor including a touchpoint refencing the NMOS Receiver resource being monitored + // JRT TODO: Add some boiler plate to add notices for unused property value holders nmos::resources& control_protocol_resources = model.control_protocol_resources; - nmos::resources& node_resources = model.node_resources; - - slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do modify_rebuildable_block"; - - // Validate the object_properties_holder - - // Find object_properties_holder for resource - const auto& filtered_holders = nmos::get_object_properties_holder(object_properties_holders, nmos::get_role_path(control_protocol_resources, resource)); - - if (filtered_holders.size() != 1) - { - auto status_message = U("Either can't find associated object_properties_holder, or there's more than one (ambiguous)"); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); - return object_properties_set_validations; - } + const auto& role_path = nmos::fields::nc::path(object_properties_holder); + const auto& allowed_member_classes = nmos::fields::nc::allowed_members_classes(object_properties_holder); - if (!nmos::nc::is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)))) + if (allowed_member_classes.size() > 0) { - auto status_message = U("Expected an NcBlock"); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); - - return object_properties_set_validations; - } - - const auto& block_members_properties_holder = nmos::get_property_value_holder(*filtered_holders.begin(), nmos::nc_block_members_property_id); - - if (block_members_properties_holder.is_null()) - { - auto status_message = U("No block members properties holder found"); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); - - return object_properties_set_validations; - } - - const auto& restore_members = nmos::fields::nc::value(block_members_properties_holder); - const auto& reference_members = nmos::fields::nc::members(resource.data); - - const auto& block_oid = nmos::fields::nc::oid(resource.data); - - std::vector members_to_remove; - std::vector members_to_add; - - // Iterate through the members of the block and compare to the members in the backup dataset - for (const auto& reference_member : reference_members) - { - auto child_role_path = web::json::value_from_elements(target_role_path); - web::json::push_back(child_role_path, nmos::fields::nc::role(reference_member)); - - const auto& filtered_members = boost::copy_range>(restore_members.as_array() - | boost::adaptors::filtered([&reference_member](const web::json::value& member) + // If allowed member classes array populated, ensure that the receiver monitor class is present + const auto& filtered_classes = boost::copy_range>(allowed_member_classes + | boost::adaptors::filtered([&](const web::json::value& member) { - return nmos::fields::nc::oid(reference_member) == nmos::fields::nc::oid(member); + return nmos::details::parse_nc_class_id(member.as_array()) == nmos::nc_receiver_monitor_class_id; }) ); - if (filtered_members.size() != 1) - { - // can't find this oid in restore dataset, so member has been removed - // get the receiver monitor resource - bool success = remove_device_model_object_handler(model, gate, nmos::fields::nc::oid(reference_member), validate); - if (success) - { - if (!validate) - { - members_to_remove.push_back(nmos::fields::nc::oid(reference_member)); - } - } - } - else + + // If receiver monitor class not allowed then return with error + if (filtered_classes.size() == 0) { - // JRT TODO: check to see if the object is rebuildable and then modify according to the filter rules - - //const auto restore_member = *filtered_members.begin(); - //if (bool(nmos::fields::nc::is_rebuildable(restore_member))) - //{ - // // Modify existing resource - // // in this example we will ignore/reject changes to the role, constant_oid, class_id or owner - // // Do nothing, return warning - // // call back to the filter_property_value_holders_handler and then modify the value - // auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); - // web::json::push_back(object_properties_set_validations, object_properties_set_validation); - // continue; - //} - //else - //{ - // Do nothing - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); - //} + auto status_message = U("Device model error: attempting to add unexpected class"); + return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::device_error, status_message); } } - // If there are any data problems they should be reported as warning/error notices - auto block_notices = web::json::value::array(); - - for (const auto& restore_member : restore_members.as_array()) + const auto& touchpoint_property_holder = nmos::get_property_value_holder(object_properties_holder, nmos::nc_object_touchpoints_property_id); + if (touchpoint_property_holder == web::json::value::null()) { - auto child_role_path = web::json::value_from_elements(target_role_path); - web::json::push_back(child_role_path, nmos::fields::nc::role(restore_member)); - - const auto& filtered_members = boost::copy_range>(reference_members - | boost::adaptors::filtered([&restore_member](const web::json::value& member) - { - return nmos::fields::nc::oid(restore_member) == nmos::fields::nc::oid(member); - }) - ); - if (filtered_members.size() != 1) - { - // JRT TODO: make this decision based on role, not OID - // can't find this oid in existing members, so member has been added - // Add this resource - // Find the object_properties_holder that describes the receiver monitor - const auto& filtered_child_object_properties_holders = boost::copy_range>(object_properties_holders - | boost::adaptors::filtered([&child_role_path](const web::json::value& object_properties_holder) - { - return nmos::fields::nc::path(object_properties_holder) == child_role_path.as_array(); - }) - ); - - if (filtered_child_object_properties_holders.size() != 1) - { - auto status_message = U("Cannot find NcObjectPropertiesHolder for new resource"); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); - - continue; - } - - const auto& child_object_properties_holder = *filtered_child_object_properties_holders.begin(); - - // JRT TODO: unsafe to take oid from backup dataset - need to create a new OID with no clash with existing OIDs so need a utility function to get "next" OID - // create utility function to get next oid - perhaps have a state in the control_protocol_state for a monotonically increasing oid - // - // - // Get member descriptor properties - auto role = nmos::fields::nc::role(restore_member); - auto oid = nmos::fields::nc::oid(restore_member); - auto owner = nmos::fields::nc::owner(restore_member); - auto constant_oid = nmos::fields::nc::constant_oid(restore_member); - const auto& block_member_description = nmos::fields::nc::description(restore_member); - const auto& block_member_user_label = nmos::fields::nc::user_label(restore_member); - - auto block_member_notices = web::json::value::array(); - - const auto& oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_oid_property_id); - if (oid_property_holder == web::json::value::null()) - { - auto status_message = U("Cannot find OID object property value holder"); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); - - continue; - } - // The values in the block member Object Property Holder will take precidence over the block member descriptor values - // If the block member values are inconsistant then warn - description and user label are independant - const auto& role_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_role_property_id); - role = role_property_holder == web::json::value::null() ? role : nmos::fields::nc::value(role_property_holder).as_string(); - - // JRT TODO: add context to error messages i.e. indicate which object has warnings - if (role_property_holder != web::json::value::null() && role != nmos::fields::nc::value(role_property_holder).as_string()) - { - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Role value in block member inconsistent with value in property holder. Property holder value takes precidence.")); - web::json::push_back(block_notices, notice); - } - if (owner != block_oid) - { - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Owner value in block member inconsistent with oid of Block. Block oid takes precidence.")); - web::json::push_back(block_notices, notice); - } - const auto& owner_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_owner_property_id); - if (owner_property_holder != web::json::value::null() && block_oid != nmos::fields::nc::value(owner_property_holder).as_integer()) - { - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Owner value in block property holder inconsistent with oid of Block. Block oid takes precidence.")); - web::json::push_back(block_member_notices, notice); - } - const auto& constant_oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_constant_oid_property_id); - constant_oid = constant_oid_property_holder == web::json::value::null() ? constant_oid : nmos::fields::nc::value(constant_oid_property_holder).as_bool(); - - if (constant_oid_property_holder != web::json::value::null() && constant_oid != nmos::fields::nc::value(constant_oid_property_holder).as_bool()) - { - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Constant OID value in block member inconsistent with value in property holder. Property holder value takes precidence.")); - web::json::push_back(block_notices, notice); - } - - const auto& user_label_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_user_label_property_id); - const auto user_label = (user_label_property_holder == web::json::value::null()) ? U("") : nmos::fields::nc::value(user_label_property_holder).as_string(); - const auto& oid2 = nmos::fields::nc::value(oid_property_holder).as_integer(); - - auto object_properties_set_validation = add_device_model_object_handler(model, gate, child_object_properties_holder, oid2, owner, role, user_label, validate); - // JRT TODO: append block_member_notices to the validation - - if (nmos::fields::nc::status(object_properties_set_validation) == nmos::nc_restore_validation_status::ok && !validate) - { - auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(block_member_description, role, oid2, constant_oid, nmos::nc_receiver_monitor_class_id, block_member_user_label, owner); - members_to_add.push_back(block_member_descriptor); - } - - web::json::push_back(object_properties_set_validations, object_properties_set_validation); - } + auto status_message = U("Cannot find touchpoint object property value holder"); + return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::failed, status_message); } - auto block_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, block_notices.as_array()); - web::json::push_back(object_properties_set_validations, block_set_validation); - // Update the members of the receivers block - if (members_to_remove.size() > 0 || members_to_add.size() > 0) + const auto& touchpoints = nmos::fields::nc::value(touchpoint_property_holder); + if (touchpoints.size() != 1) { - auto modified_members = web::json::value::array(); - - for (const auto& member : reference_members) - { - const auto& remove_member = boost::copy_range>(members_to_remove | boost::adaptors::filtered([&member](int oid) - { - return oid == nmos::fields::nc::oid(member); - }) - ); - - if (remove_member.size() == 0) - { - web::json::push_back(modified_members, member); - } - } - for (const auto& member : members_to_add) - { - web::json::push_back(modified_members, member); - } - - nmos::nc::modify_resource(control_protocol_resources, resource.id, [&](nmos::resource& resource) - { - resource.data[nmos::fields::nc::members] = modified_members; - - }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { { nmos::nc_block_members_property_id, nmos::nc_property_change_type::type::value_changed, modified_members } })); + auto status_message = U("Either zero or more than one touchpoint found (ambiguous)."); + return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::failed, status_message); } + if (!validate) // If validate is true then don't add object to device model, just indicate whether it's possible given the data supplied + { + const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); - return object_properties_set_validations; + auto receiver_monitor = nmos::make_receiver_monitor(oid, true, owner, role, user_label, U(""), web::json::value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})} })); + nmos::nc::insert_resource(control_protocol_resources, std::move(receiver_monitor)); + } + return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::ok); }; } @@ -2254,5 +2016,6 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode .on_channelmapping_activated(make_node_implementation_channelmapping_activation_handler(gate)) .on_control_protocol_property_changed(make_node_implementation_control_protocol_property_changed_handler(gate)) // may be omitted if IS-12 not required .on_filter_property_value_holders(make_filter_property_value_holders_handler(model.control_protocol_resources, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required - .on_modify_rebuildable_block(make_modify_rebuildable_block_handler(model, gate)); // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required + .on_remove_device_model_object(make_remove_device_model_handler(model, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required + .on_add_device_model_object(make_add_device_model_object_handler(model, gate)); // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required } diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 29abb7b82..881b26ca6 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -18,25 +18,25 @@ namespace nmos { - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { using namespace web::http::experimental::listener::api_router_using_declarations; api_router configuration_api; configuration_api.support(U("/?"), methods::GET, [](http_request req, http_response res, const string_t&, const route_parameters&) - { - set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("x-nmos/") }, req, res)); - return pplx::task_from_result(true); - }); + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("x-nmos/") }, req, res)); + return pplx::task_from_result(true); + }); configuration_api.support(U("/x-nmos/?"), methods::GET, [](http_request req, http_response res, const string_t&, const route_parameters&) - { - set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("configuration/") }, req, res)); - return pplx::task_from_result(true); - }); + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("configuration/") }, req, res)); + return pplx::task_from_result(true); + }); if (validate_authorization) { @@ -46,12 +46,12 @@ namespace nmos const auto versions = with_read_lock(model.mutex, [&model] { return nmos::is14_versions::from_settings(model.settings); }); configuration_api.support(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/?"), methods::GET, [versions](http_request req, http_response res, const string_t&, const route_parameters&) - { - set_reply(res, status_codes::OK, nmos::make_sub_routes_body(nmos::make_api_version_sub_routes(versions), req, res)); - return pplx::task_from_result(true); - }); + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body(nmos::make_api_version_sub_routes(versions), req, res)); + return pplx::task_from_result(true); + }); - configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, filter_property_value_holders, modify_rebuildable_block, property_changed, gate)); + configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object, property_changed, gate)); return configuration_api; } @@ -148,7 +148,7 @@ namespace nmos } } - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -159,392 +159,285 @@ namespace nmos configuration_api.support(U(".*"), details::make_api_version_handler(versions, gate_)); configuration_api.support(U("/?"), methods::GET, [](http_request req, http_response res, const string_t&, const route_parameters&) - { - set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("rolePaths/") }, req, res)); - return pplx::task_from_result(true); - }); + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("rolePaths/") }, req, res)); + return pplx::task_from_result(true); + }); // GET /rolePaths configuration_api.support(U("/rolePaths/?"), methods::GET, [&model](http_request req, http_response res, const string_t&, const route_parameters&) - { - auto lock = model.read_lock(); - auto& resources = model.control_protocol_resources; + { + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; - std::set role_paths; + std::set role_paths; - // start at the root block resource - auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); - if (resources.end() != resource) - { - // add root to role_paths - const auto role_path = nmos::fields::nc::role(resource->data); - role_paths.insert(role_path + U("/")); + // start at the root block resource + auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::root_block_oid))); + if (resources.end() != resource) + { + // add root to role_paths + const auto role_path = nmos::fields::nc::role(resource->data); + role_paths.insert(role_path + U("/")); - // add rest to the role_paths - details::build_role_paths(resources, *resource, role_path, role_paths); - } + // add rest to the role_paths + details::build_role_paths(resources, *resource, role_path, role_paths); + } - set_reply(res, status_codes::OK, nmos::make_sub_routes_body(role_paths, req, res)); - return pplx::task_from_result(true); - }); + set_reply(res, status_codes::OK, nmos::make_sub_routes_body(role_paths, req, res)); + return pplx::task_from_result(true); + }); // GET /rolePaths/{rolePath} configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/?"), methods::GET, [&model, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) - { - const auto role_path = parameters.at(nmos::patterns::rolePath.name); + { + const auto role_path = parameters.at(nmos::patterns::rolePath.name); - auto lock = model.read_lock(); - auto& resources = model.control_protocol_resources; - const auto& resource = nc::find_resource_by_role_path(resources, role_path); + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; + const auto& resource = nc::find_resource_by_role_path(resources, role_path); - if (resources.end() != resource) - { - set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("bulkProperties/"), U("descriptor/"), U("methods/"), U("properties/") }, req, res)); - } - else - { - // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); - set_reply(res, status_codes::NotFound, method_result); - } + if (resources.end() != resource) + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("bulkProperties/"), U("descriptor/"), U("methods/"), U("properties/") }, req, res)); + } + else + { + // resource not found for the role path + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); + } - return pplx::task_from_result(true); - }); + return pplx::task_from_result(true); + }); // GET /rolePaths/{rolePath}/properties configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) - { - const auto role_path = parameters.at(nmos::patterns::rolePath.name); - - auto lock = model.read_lock(); - auto& resources = model.control_protocol_resources; - - const auto& resource = nc::find_resource_by_role_path(resources, role_path); - if (resources.end() != resource) { - std::set properties_routes; + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; - auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); - while (!class_id.empty()) + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) { - const auto& control_class = get_control_protocol_class_descriptor(class_id); - auto& property_descriptors = control_class.property_descriptors.as_array(); + std::set properties_routes; - auto properties_route = boost::copy_range>(property_descriptors | boost::adaptors::transformed([](const web::json::value& property_descriptor) + auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); + while (!class_id.empty()) { - return details::make_formatted_property_id(property_descriptor) + U("/"); - })); + const auto& control_class = get_control_protocol_class_descriptor(class_id); + auto& property_descriptors = control_class.property_descriptors.as_array(); - properties_routes.insert(properties_route.begin(), properties_route.end()); + auto properties_route = boost::copy_range>(property_descriptors | boost::adaptors::transformed([](const web::json::value& property_descriptor) + { + return details::make_formatted_property_id(property_descriptor) + U("/"); + })); - class_id.pop_back(); - } + properties_routes.insert(properties_route.begin(), properties_route.end()); - set_reply(res, status_codes::OK, nmos::make_sub_routes_body(properties_routes, req, res)); - } - else - { - // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); - set_reply(res, status_codes::NotFound, method_result); - } + class_id.pop_back(); + } + + set_reply(res, status_codes::OK, nmos::make_sub_routes_body(properties_routes, req, res)); + } + else + { + // resource not found for the role path + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); + } - return pplx::task_from_result(true); - }); + return pplx::task_from_result(true); + }); // GET /rolePaths/{rolePath}/methods configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/methods/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) - { - const auto role_path = parameters.at(nmos::patterns::rolePath.name); - - auto lock = model.read_lock(); - auto& resources = model.control_protocol_resources; - - const auto& resource = nc::find_resource_by_role_path(resources, role_path); - if (resources.end() != resource) { - std::set methods_routes; + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; - auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); - while (!class_id.empty()) + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) { - const auto& control_class = get_control_protocol_class_descriptor(class_id); - auto& method_descriptors = control_class.method_descriptors; + std::set methods_routes; - auto methods_route = boost::copy_range>(method_descriptors | boost::adaptors::transformed([](const nmos::experimental::method& method) + auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); + while (!class_id.empty()) { - auto make_method_id = [](const nmos::experimental::method& method) - { - // method tuple definition described in control_protocol_handlers.h - auto& nc_method_descriptor = std::get<0>(method); - return details::make_formatted_method_id(nc_method_descriptor); - }; + const auto& control_class = get_control_protocol_class_descriptor(class_id); + auto& method_descriptors = control_class.method_descriptors; - return make_method_id(method) + U("/"); - })); + auto methods_route = boost::copy_range>(method_descriptors | boost::adaptors::transformed([](const nmos::experimental::method& method) + { + auto make_method_id = [](const nmos::experimental::method& method) + { + // method tuple definition described in control_protocol_handlers.h + auto& nc_method_descriptor = std::get<0>(method); + return details::make_formatted_method_id(nc_method_descriptor); + }; - methods_routes.insert(methods_route.begin(), methods_route.end()); + return make_method_id(method) + U("/"); + })); - class_id.pop_back(); - } + methods_routes.insert(methods_route.begin(), methods_route.end()); - set_reply(res, status_codes::OK, nmos::make_sub_routes_body(methods_routes, req, res)); - } - else - { - // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); - set_reply(res, status_codes::NotFound, method_result); - } + class_id.pop_back(); + } + + set_reply(res, status_codes::OK, nmos::make_sub_routes_body(methods_routes, req, res)); + } + else + { + // resource not found for the role path + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); + } - return pplx::task_from_result(true); - }); + return pplx::task_from_result(true); + }); // GET /rolePaths/{rolePath}/descriptor configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/descriptor/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) - { - const auto role_path = parameters.at(nmos::patterns::rolePath.name); - - auto lock = model.read_lock(); - auto& resources = model.control_protocol_resources; - - const auto& resource = nc::find_resource_by_role_path(resources, role_path); - if (resources.end() != resource) { - nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); - - if (!class_id.empty()) - { - // Hmmm, this feels like it should be a utility function, or we just parameterize the handler to include inherited - const auto& control_class = get_control_protocol_class_descriptor(class_id); + const auto role_path = parameters.at(nmos::patterns::rolePath.name); - auto& description = control_class.description; - auto& name = control_class.name; - auto& fixed_role = control_class.fixed_role; - auto property_descriptors = control_class.property_descriptors; - auto method_descriptors = value::array(); - for (const auto& method_descriptor : control_class.method_descriptors) { web::json::push_back(method_descriptors, std::get<0>(method_descriptor)); } - auto event_descriptors = control_class.event_descriptors; + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; - auto inherited_class_id = class_id; - inherited_class_id.pop_back(); + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) + { + nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); - while (!inherited_class_id.empty()) + if (!class_id.empty()) { - const auto& inherited_control_class = get_control_protocol_class_descriptor(inherited_class_id); + // Hmmm, this feels like it should be a utility function, or we just parameterize the handler to include inherited + const auto& control_class = get_control_protocol_class_descriptor(class_id); + + auto& description = control_class.description; + auto& name = control_class.name; + auto& fixed_role = control_class.fixed_role; + auto property_descriptors = control_class.property_descriptors; + auto method_descriptors = value::array(); + for (const auto& method_descriptor : control_class.method_descriptors) { web::json::push_back(method_descriptors, std::get<0>(method_descriptor)); } + auto event_descriptors = control_class.event_descriptors; + + auto inherited_class_id = class_id; + inherited_class_id.pop_back(); + + while (!inherited_class_id.empty()) { - for (const auto& property_descriptor : inherited_control_class.property_descriptors.as_array()) { web::json::push_back(property_descriptors, property_descriptor); } - for (const auto& method_descriptor : inherited_control_class.method_descriptors) { web::json::push_back(method_descriptors, std::get<0>(method_descriptor)); } - for (const auto& event_descriptor : inherited_control_class.event_descriptors.as_array()) { web::json::push_back(event_descriptors, event_descriptor); } + const auto& inherited_control_class = get_control_protocol_class_descriptor(inherited_class_id); + { + for (const auto& property_descriptor : inherited_control_class.property_descriptors.as_array()) { web::json::push_back(property_descriptors, property_descriptor); } + for (const auto& method_descriptor : inherited_control_class.method_descriptors) { web::json::push_back(method_descriptors, std::get<0>(method_descriptor)); } + for (const auto& event_descriptor : inherited_control_class.event_descriptors.as_array()) { web::json::push_back(event_descriptors, event_descriptor); } + } + inherited_class_id.pop_back(); } - inherited_class_id.pop_back(); - } - auto class_descriptor = fixed_role.is_null() - ? details::make_nc_class_descriptor(description, class_id, name, property_descriptors, method_descriptors, event_descriptors) - : details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), property_descriptors, method_descriptors, event_descriptors); + auto class_descriptor = fixed_role.is_null() + ? details::make_nc_class_descriptor(description, class_id, name, property_descriptors, method_descriptors, event_descriptors) + : details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), property_descriptors, method_descriptors, event_descriptors); - auto method_result = details::make_nc_method_result({ nmos::nc_method_status::ok }, class_descriptor); - set_reply(res, status_codes::OK, method_result); + auto method_result = details::make_nc_method_result({ nmos::nc_method_status::ok }, class_descriptor); + set_reply(res, status_codes::OK, method_result); + } } - } - else - { - // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); - set_reply(res, status_codes::NotFound, method_result); - } - - return pplx::task_from_result(true); - }); - - // GET /rolePaths/{rolePath}/properties/{propertyId} - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) - { - const auto property_id = parameters.at(nmos::patterns::propertyId.name); - const auto role_path = parameters.at(nmos::patterns::rolePath.name); - - auto lock = model.read_lock(); - auto& resources = model.control_protocol_resources; - - const auto& resource = nc::find_resource_by_role_path(resources, role_path); - if (resources.end() != resource) - { - // find the relevant nc_property_descriptor - const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); - if (property_descriptor.is_null()) + else { // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::property_not_implemented }, U("Not Found; ") + property_id); + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } - else - { - set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("descriptor/"), U("value/") }, req, res)); - } - } - else - { - // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); - set_reply(res, status_codes::NotFound, method_result); - } - return pplx::task_from_result(true); - }); - - // GET /rolePaths/{rolePath}/properties/{propertyId}/descriptor - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/descriptor/?"), methods::GET, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) - { - const auto property_id = parameters.at(nmos::patterns::propertyId.name); - const auto role_path = parameters.at(nmos::patterns::rolePath.name); - - auto lock = model.read_lock(); - auto& resources = model.control_protocol_resources; + return pplx::task_from_result(true); + }); - const auto& resource = nc::find_resource_by_role_path(resources, role_path); - if (resources.end() != resource) + // GET /rolePaths/{rolePath}/properties/{propertyId} + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { - // find the relevant nc_property_descriptor - const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); - const auto& property_type = nmos::fields::nc::type_name(property_descriptor); - auto datatype_descriptor = nc::details::get_datatype_descriptor(value::string(property_type), get_control_protocol_datatype_descriptor); + const auto property_id = parameters.at(nmos::patterns::propertyId.name); + const auto role_path = parameters.at(nmos::patterns::rolePath.name); - if (nmos::nc_datatype_type::Struct == nmos::fields::nc::type(datatype_descriptor)) - { - auto inherited_struct = datatype_descriptor; + auto lock = model.read_lock(); + auto& resources = model.control_protocol_resources; - while (!nmos::fields::nc::parent_type(inherited_struct).is_null()) + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) + { + // find the relevant nc_property_descriptor + const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); + if (property_descriptor.is_null()) { - auto parent_type = nmos::fields::nc::parent_type(datatype_descriptor).as_string(); - - inherited_struct = nc::details::get_datatype_descriptor(value::string(parent_type), get_control_protocol_datatype_descriptor); - - for (const auto field : nmos::fields::nc::fields(inherited_struct)) - { - web::json::push_back(datatype_descriptor[nmos::fields::nc::fields], field); - } + // resource not found for the role path + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::property_not_implemented }, U("Not Found; ") + property_id); + set_reply(res, status_codes::NotFound, method_result); + } + else + { + set_reply(res, status_codes::OK, nmos::make_sub_routes_body({ U("descriptor/"), U("value/") }, req, res)); } - } - - if (property_descriptor.is_null()) - { - // property not found - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::property_not_implemented }, U("Not Found; ") + property_id); - set_reply(res, status_codes::NotFound, method_result); } else { - auto method_result = details::make_nc_method_result({ nmos::nc_method_status::ok }, datatype_descriptor); - set_reply(res, status_codes::OK, method_result); + // resource not found for the role path + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); } - } - else - { - // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); - set_reply(res, status_codes::NotFound, method_result); - } - - return pplx::task_from_result(true); - }); - - // GET /rolePaths/{rolePath}/properties/{propertyId}/value - invokes get method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/value/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) - { - const auto property_id = parameters.at(nmos::patterns::propertyId.name); - const auto role_path = parameters.at(nmos::patterns::rolePath.name); - - auto lock = model.read_lock(); - auto& resources = model.control_protocol_resources; - const auto& resource = nc::find_resource_by_role_path(resources, role_path); - if (resources.end() != resource) - { - auto arguments = value_of({ - { nmos::fields::nc::id, details::make_nc_property_id(details::parse_formatted_property_id(property_id))}, - }); - - auto result = get(*resource, arguments, false, get_control_protocol_class_descriptor, gate_); - auto status = nmos::fields::nc::status(result); - auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; - set_reply(res, code, result); - } - else - { - // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); - set_reply(res, status_codes::NotFound, method_result); - } - - return pplx::task_from_result(true); - }); + return pplx::task_from_result(true); + }); - // GET /rolePaths/{rolePath}/methods/{methodId} - invokes method specified by {methodId} - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/methods/") + nmos::patterns::methodId.pattern + U("/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, property_changed, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) - { - nmos::api_gate gate(gate_, req, parameters); - return details::extract_json(req, gate).then([&model, req, res, parameters, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate](value body) mutable + // GET /rolePaths/{rolePath}/properties/{propertyId}/descriptor + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/descriptor/?"), methods::GET, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { - auto lock = model.write_lock(); - - const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - - // Validate JSON syntax according to the schema - details::configurationapi_validator().validate(body, experimental::make_configurationapi_method_patch_request_schema_uri(version)); - + const auto property_id = parameters.at(nmos::patterns::propertyId.name); const auto role_path = parameters.at(nmos::patterns::rolePath.name); - const auto method_id = parameters.at(nmos::patterns::methodId.name); + auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; - auto& arguments = nmos::fields::nc::arguments(body); const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { - auto method = get_control_protocol_method_descriptor(details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), details::parse_formatted_method_id(method_id)); - auto& nc_method_descriptor = method.first; - auto& control_method_handler = method.second; - web::http::status_code code{ status_codes::BadRequest }; - value method_result; + // find the relevant nc_property_descriptor + const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); + const auto& property_type = nmos::fields::nc::type_name(property_descriptor); + auto datatype_descriptor = nc::details::get_datatype_descriptor(value::string(property_type), get_control_protocol_datatype_descriptor); - if (control_method_handler) + if (nmos::nc_datatype_type::Struct == nmos::fields::nc::type(datatype_descriptor)) { - try + auto inherited_struct = datatype_descriptor; + + while (!nmos::fields::nc::parent_type(inherited_struct).is_null()) { - // do method arguments constraints validation - nc::method_parameters_contraints_validation(arguments, nc_method_descriptor, get_control_protocol_datatype_descriptor); + auto parent_type = nmos::fields::nc::parent_type(datatype_descriptor).as_string(); - // execute the relevant control method handler, then accumulating up their response to reponses - method_result = control_method_handler(resources, *resource, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), gate); + inherited_struct = nc::details::get_datatype_descriptor(value::string(parent_type), get_control_protocol_datatype_descriptor); - auto status = nmos::fields::nc::status(method_result); - if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } - else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } - else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } - else { code = status_codes::InternalError; } + for (const auto field : nmos::fields::nc::fields(inherited_struct)) + { + web::json::push_back(datatype_descriptor[nmos::fields::nc::fields], field); + } } - catch (const nmos::control_protocol_exception& e) - { - // invalid arguments - utility::stringstream_t ss; - ss << U("invalid argument: ") << arguments.serialize() << " error: " << e.what(); - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + } - code = status_codes::BadRequest; - } + if (property_descriptor.is_null()) + { + // property not found + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::property_not_implemented }, U("Not Found; ") + property_id); + set_reply(res, status_codes::NotFound, method_result); } else { - // unknown methodId - utility::stringstream_t ss; - ss << U("unsupported method_id: ") << method_id - << U(" for control class class_id: ") << resource->data.at(nmos::fields::nc::class_id).serialize(); - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, ss.str()); - - code = status_codes::NotFound; + auto method_result = details::make_nc_method_result({ nmos::nc_method_status::ok }, datatype_descriptor); + set_reply(res, status_codes::OK, method_result); } - set_reply(res, code, method_result); } else { @@ -553,53 +446,29 @@ namespace nmos set_reply(res, status_codes::NotFound, method_result); } - return true; + return pplx::task_from_result(true); }); - }); - // PUT /rolePaths/{rolePath}/properties/{propertyId}/value - invokes set method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/value/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) - { - nmos::api_gate gate(gate_, req, parameters); - return details::extract_json(req, gate).then([&model, req, res, parameters, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate](value body) mutable + // GET /rolePaths/{rolePath}/properties/{propertyId}/value - invokes get method + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/value/?"), methods::GET, [&model, get_control_protocol_class_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { - auto lock = model.write_lock(); - - const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - - // Validate JSON syntax according to the schema - details::configurationapi_validator().validate(body, experimental::make_configurationapi_property_value_put_request_schema_uri(version)); - - const auto role_path = parameters.at(nmos::patterns::rolePath.name); const auto property_id = parameters.at(nmos::patterns::propertyId.name); + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { - // find the relevant nc_property_descriptor - const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); - if (property_descriptor.is_null()) - { - // property not found - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + property_id); - set_reply(res, status_codes::NotFound, method_result); - } - else - { - auto arguments = value_of({ - { nmos::fields::nc::id, details::make_nc_property_id(details::parse_formatted_property_id(property_id))}, - { nmos::fields::nc::value, nmos::fields::nc::value(body)} + auto arguments = value_of({ + { nmos::fields::nc::id, details::make_nc_property_id(details::parse_formatted_property_id(property_id))}, }); - auto result = set(resources, *resource, arguments, false, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate); - - auto status = nmos::fields::nc::status(result); - auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; - set_reply(res, code, result); - model.notify(); - } + auto result = get(*resource, arguments, false, get_control_protocol_class_descriptor, gate_); + auto status = nmos::fields::nc::status(result); + auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; + set_reply(res, code, result); } else { @@ -608,68 +477,150 @@ namespace nmos set_reply(res, status_codes::NotFound, method_result); } - return true; + return pplx::task_from_result(true); }); - }); - // GET /rolePaths/{rolePath}/bulkProperties - invokes get_properties_by_path method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::GET, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) - { - const auto role_path = parameters.at(nmos::patterns::rolePath.name); + // GET /rolePaths/{rolePath}/methods/{methodId} - invokes method specified by {methodId} + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/methods/") + nmos::patterns::methodId.pattern + U("/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, property_changed, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + nmos::api_gate gate(gate_, req, parameters); + return details::extract_json(req, gate).then([&model, req, res, parameters, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate](value body) mutable + { + auto lock = model.write_lock(); - auto lock = model.read_lock(); - auto& resources = model.control_protocol_resources; - const auto& resource = nc::find_resource_by_role_path(resources, role_path); + const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - if (resources.end() != resource) - { - web::http::status_code code{ status_codes::BadRequest }; - value method_result; + // Validate JSON syntax according to the schema + details::configurationapi_validator().validate(body, experimental::make_configurationapi_method_patch_request_schema_uri(version)); - try - { - bool recurse = details::parse_recurse_query_parameter(req.request_uri().query()); + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + const auto method_id = parameters.at(nmos::patterns::methodId.name); - method_result = get_properties_by_path(resources, *resource, recurse, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + auto& resources = model.control_protocol_resources; + auto& arguments = nmos::fields::nc::arguments(body); - auto status = nmos::fields::nc::status(method_result); - if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } - else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } - else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } - else { code = status_codes::InternalError; } - } - catch (const nmos::control_protocol_exception& e) - { - // invalid arguments - utility::stringstream_t ss; - ss << U("parameter error: ") << e.what(); - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) + { + auto method = get_control_protocol_method_descriptor(details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), details::parse_formatted_method_id(method_id)); + auto& nc_method_descriptor = method.first; + auto& control_method_handler = method.second; + web::http::status_code code{ status_codes::BadRequest }; + value method_result; + + if (control_method_handler) + { + try + { + // do method arguments constraints validation + nc::method_parameters_contraints_validation(arguments, nc_method_descriptor, get_control_protocol_datatype_descriptor); + + // execute the relevant control method handler, then accumulating up their response to reponses + method_result = control_method_handler(resources, *resource, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), gate); + + auto status = nmos::fields::nc::status(method_result); + if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } + else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } + else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } + else { code = status_codes::InternalError; } + } + catch (const nmos::control_protocol_exception& e) + { + // invalid arguments + utility::stringstream_t ss; + ss << U("invalid argument: ") << arguments.serialize() << " error: " << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + + code = status_codes::BadRequest; + } + } + else + { + // unknown methodId + utility::stringstream_t ss; + ss << U("unsupported method_id: ") << method_id + << U(" for control class class_id: ") << resource->data.at(nmos::fields::nc::class_id).serialize(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, ss.str()); + + code = status_codes::NotFound; + } + set_reply(res, code, method_result); + } + else + { + // resource not found for the role path + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); + } - code = status_codes::BadRequest; - } - set_reply(res, code, method_result); - } - else + return true; + }); + }); + + // PUT /rolePaths/{rolePath}/properties/{propertyId}/value - invokes set method + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/properties/") + nmos::patterns::propertyId.pattern + U("/value/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { - // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); - set_reply(res, status_codes::NotFound, method_result); - } + nmos::api_gate gate(gate_, req, parameters); + return details::extract_json(req, gate).then([&model, req, res, parameters, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate](value body) mutable + { + auto lock = model.write_lock(); - return pplx::task_from_result(true); - }); + const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - // PATCH /rolePaths/{rolePath}/bulkProperties - invokes validation_set_properties_by_path method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) - { - const auto role_path = parameters.at(nmos::patterns::rolePath.name); - const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); + // Validate JSON syntax according to the schema + details::configurationapi_validator().validate(body, experimental::make_configurationapi_property_value_put_request_schema_uri(version)); + + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + const auto property_id = parameters.at(nmos::patterns::propertyId.name); + + auto& resources = model.control_protocol_resources; + + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) + { + // find the relevant nc_property_descriptor + const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); + if (property_descriptor.is_null()) + { + // property not found + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + property_id); + set_reply(res, status_codes::NotFound, method_result); + } + else + { + auto arguments = value_of({ + { nmos::fields::nc::id, details::make_nc_property_id(details::parse_formatted_property_id(property_id))}, + { nmos::fields::nc::value, nmos::fields::nc::value(body)} + }); + + auto result = set(resources, *resource, arguments, false, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate); + + auto status = nmos::fields::nc::status(result); + auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; + set_reply(res, code, result); + model.notify(); + } + } + else + { + // resource not found for the role path + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); + } + + return true; + }); + }); - return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, version, &gate_](value body) mutable + // GET /rolePaths/{rolePath}/bulkProperties - invokes get_properties_by_path method + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::GET, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { - auto lock = model.write_lock(); + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + + auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) { web::http::status_code code{ status_codes::BadRequest }; @@ -677,15 +628,9 @@ namespace nmos try { - // Validate JSON syntax according to the schema - details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_validate_request_schema_uri(version)); + bool recurse = details::parse_recurse_query_parameter(req.request_uri().query()); - const auto& arguments = nmos::fields::nc::arguments(body); - bool recurse = nmos::fields::nc::recurse(arguments); - const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); - const auto& backup_data_set = nmos::fields::nc::data_set(arguments); - - method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + method_result = get_properties_by_path(resources, *resource, recurse, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); auto status = nmos::fields::nc::status(method_result); if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } @@ -702,15 +647,6 @@ namespace nmos code = status_codes::BadRequest; } - catch (const web::json::json_exception& e) - { - // JSON validation error - utility::stringstream_t ss; - ss << U("JSON validation error: ") << e.what(); - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); - - code = status_codes::BadRequest; - } set_reply(res, code, method_result); } else @@ -719,72 +655,136 @@ namespace nmos auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } - return true; - }); - }); - // PUT /rolePaths/{rolePath}/bulkProperties - invokes set_properties_by_path method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) - { - const auto role_path = parameters.at(nmos::patterns::rolePath.name); - const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); + return pplx::task_from_result(true); + }); - return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block, version, &gate_](value body) mutable + // PATCH /rolePaths/{rolePath}/bulkProperties - invokes validation_set_properties_by_path method + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { - auto lock = model.write_lock(); - auto& resources = model.control_protocol_resources; - const auto& resource = nc::find_resource_by_role_path(resources, role_path); - if (resources.end() != resource) - { - web::http::status_code code{ status_codes::BadRequest }; - value method_result; + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - try + return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object, version, &gate_](value body) mutable { - // Validate JSON syntax according to the schema - details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_set_request_schema_uri(version)); - - const auto& arguments = nmos::fields::nc::arguments(body); - bool recurse = nmos::fields::nc::recurse(arguments); - const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); - const auto& backup_data_set = nmos::fields::nc::data_set(arguments); - - method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); - - code = status_codes::OK; + auto lock = model.write_lock(); + auto& resources = model.control_protocol_resources; + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) + { + web::http::status_code code{ status_codes::BadRequest }; + value method_result; + + try + { + // Validate JSON syntax according to the schema + details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_validate_request_schema_uri(version)); + + const auto& arguments = nmos::fields::nc::arguments(body); + bool recurse = nmos::fields::nc::recurse(arguments); + const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); + const auto& backup_data_set = nmos::fields::nc::data_set(arguments); + + method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + + auto status = nmos::fields::nc::status(method_result); + if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } + else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } + else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } + else { code = status_codes::InternalError; } + } + catch (const nmos::control_protocol_exception& e) + { + // invalid arguments + utility::stringstream_t ss; + ss << U("parameter error: ") << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + + code = status_codes::BadRequest; + } + catch (const web::json::json_exception& e) + { + // JSON validation error + utility::stringstream_t ss; + ss << U("JSON validation error: ") << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + + code = status_codes::BadRequest; + } + set_reply(res, code, method_result); + } + else + { + // resource not found for the role path + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); + } + return true; + }); + }); - model.notify(); - } - catch (const nmos::control_protocol_exception& e) - { - // invalid arguments - utility::stringstream_t ss; - ss << U("parameter error: ") << e.what(); - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + // PUT /rolePaths/{rolePath}/bulkProperties - invokes set_properties_by_path method + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + { + const auto role_path = parameters.at(nmos::patterns::rolePath.name); + const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - code = status_codes::BadRequest; - } - catch (const web::json::json_exception& e) + return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object, version, &gate_](value body) mutable { - // JSON validation error - utility::stringstream_t ss; - ss << U("JSON validation error: ") << e.what(); - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); - - code = status_codes::BadRequest; - } - set_reply(res, code, method_result); - } - else - { - // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); - set_reply(res, status_codes::NotFound, method_result); - } + auto lock = model.write_lock(); + auto& resources = model.control_protocol_resources; + const auto& resource = nc::find_resource_by_role_path(resources, role_path); + if (resources.end() != resource) + { + web::http::status_code code{ status_codes::BadRequest }; + value method_result; + + try + { + // Validate JSON syntax according to the schema + details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_set_request_schema_uri(version)); + + const auto& arguments = nmos::fields::nc::arguments(body); + bool recurse = nmos::fields::nc::recurse(arguments); + const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); + const auto& backup_data_set = nmos::fields::nc::data_set(arguments); + + method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + + code = status_codes::OK; + + model.notify(); + } + catch (const nmos::control_protocol_exception& e) + { + // invalid arguments + utility::stringstream_t ss; + ss << U("parameter error: ") << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + + code = status_codes::BadRequest; + } + catch (const web::json::json_exception& e) + { + // JSON validation error + utility::stringstream_t ss; + ss << U("JSON validation error: ") << e.what(); + method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + + code = status_codes::BadRequest; + } + set_reply(res, code, method_result); + } + else + { + // resource not found for the role path + auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + set_reply(res, status_codes::NotFound, method_result); + } - return true; + return true; + }); }); - }); return configuration_api; } diff --git a/Development/nmos/configuration_api.h b/Development/nmos/configuration_api.h index 5bf4df971..dc967c2e9 100644 --- a/Development/nmos/configuration_api.h +++ b/Development/nmos/configuration_api.h @@ -16,7 +16,7 @@ namespace nmos { struct node_model; - web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); } #endif diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h index 7ca6940e7..99690969e 100644 --- a/Development/nmos/configuration_handlers.h +++ b/Development/nmos/configuration_handlers.h @@ -25,6 +25,16 @@ namespace nmos // This function should handle the modification of the Device Model and any corresponding NMOS resources // and return correpsonding NcObjectPropertiesSetValidation objects for each object modified/added typedef std::function modify_rebuildable_block_handler; + + // This callback is invoked if attempting to remove a device model object when restoring a configuration. + // This function should handle the modification of the Device Model and any corresponding NMOS resources + // and return true if successful and false otherwise + typedef std::function remove_device_model_object_handler; + + // This callback is invoked if attempting to add a device model object to a rebuildable block when restoring a configuration. + // This function should handle the modification of the Device Model and any corresponding NMOS resources + // and return correpsonding NcObjectPropertiesSetValidation objects for the object added + typedef std::function add_device_model_object_handler; } #endif diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index 6fc03f4ff..31f18ff36 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -124,24 +124,23 @@ namespace nmos return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, bulk_values_holder); } - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) { // Do something with validation fingerprint? const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } - web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) { // Do something with validation fingerprint? const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } - } \ No newline at end of file diff --git a/Development/nmos/configuration_methods.h b/Development/nmos/configuration_methods.h index 65d9eaa41..cf6323c75 100644 --- a/Development/nmos/configuration_methods.h +++ b/Development/nmos/configuration_methods.h @@ -17,9 +17,9 @@ namespace nmos // Implementation of IS-14 function for creating backup dataset from a Device Model web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor); - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); - web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); } #endif diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index cc4b37c99..6c525c972 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -4,6 +4,7 @@ #include "cpprest/json_utils.h" #include "nmos/configuration_handlers.h" #include "nmos/configuration_resources.h" +#include "nmos/configuration_utils.h" #include "nmos/control_protocol_resource.h" #include "nmos/control_protocol_resources.h" #include "nmos/control_protocol_state.h" @@ -70,6 +71,243 @@ namespace nmos } return false; } + + web::json::value modify_rebuildable_block(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) + { + // rebuildable block and child objects are passed to this function for modification + auto object_properties_set_validations = web::json::value::array(); + + // Find object_properties_holder for resource + const auto& filtered_holders = nmos::get_object_properties_holder(object_properties_holders, nmos::get_role_path(resources, resource)); + + if (filtered_holders.size() != 1) + { + auto status_message = U("Either can't find associated object_properties_holder, or there's more than one (ambiguous)"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + return object_properties_set_validations; + } + + if (!nmos::nc::is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)))) + { + auto status_message = U("Expected an NcBlock"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + return object_properties_set_validations; + } + + const auto& block_members_properties_holder = nmos::get_property_value_holder(*filtered_holders.begin(), nmos::nc_block_members_property_id); + + if (block_members_properties_holder.is_null()) + { + // JRT TODO: just update the block properties in this case + auto status_message = U("No block members properties holder found"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + return object_properties_set_validations; + } + + const auto& restore_members = nmos::fields::nc::value(block_members_properties_holder); + const auto& reference_members = nmos::fields::nc::members(resource.data); + + const auto& block_oid = nmos::fields::nc::oid(resource.data); + + std::vector members_to_remove; + std::vector members_to_add; + + // Iterate through the members of the block and compare to the members in the backup dataset + for (const auto& reference_member : reference_members) + { + auto child_role_path = web::json::value_from_elements(target_role_path); + web::json::push_back(child_role_path, nmos::fields::nc::role(reference_member)); + + const auto& filtered_members = boost::copy_range>(restore_members.as_array() + | boost::adaptors::filtered([&reference_member](const web::json::value& member) + { + return nmos::fields::nc::role(reference_member) == nmos::fields::nc::role(member); + }) + ); + if (filtered_members.size() != 1) + { + // can't find this role in restore dataset, so member has been removed + // get the receiver monitor resource + bool success = remove_device_model_object(nmos::fields::nc::oid(reference_member), validate); + if (success) + { + if (!validate) + { + members_to_remove.push_back(nmos::fields::nc::oid(reference_member)); + } + } + else + { + // unable to delete resource so stop updating block and report the error + auto notices = web::json::value::array(); + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Unable to delete resource from Device Model.")); + web::json::push_back(notices, notice); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::device_error, notices.as_array(), U("Unable to delete resource from Device Model")); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + return object_properties_set_validations; + } + } + } + // If there are any data problems they should be reported as warning/error notices + auto block_notices = web::json::value::array(); + + for (const auto& restore_member : restore_members.as_array()) + { + auto child_role_path = web::json::value_from_elements(target_role_path); + web::json::push_back(child_role_path, nmos::fields::nc::role(restore_member)); + + const auto& filtered_members = boost::copy_range>(reference_members + | boost::adaptors::filtered([&restore_member](const web::json::value& member) + { + return nmos::fields::nc::role(restore_member) == nmos::fields::nc::role(member); + }) + ); + if (filtered_members.size() != 1) + { + // can't find this role in existing members, so member has been added + // Add this resource + // Find the object_properties_holder that describes the receiver monitor + const auto& filtered_child_object_properties_holders = boost::copy_range>(object_properties_holders + | boost::adaptors::filtered([&child_role_path](const web::json::value& object_properties_holder) + { + return nmos::fields::nc::path(object_properties_holder) == child_role_path.as_array(); + }) + ); + + if (filtered_child_object_properties_holders.size() != 1) + { + auto status_message = U("Cannot find NcObjectPropertiesHolder for new resource"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + continue; + } + + const auto& child_object_properties_holder = *filtered_child_object_properties_holders.begin(); + + // JRT TODO: unsafe to take oid from backup dataset - need to create a new OID with no clash with existing OIDs so need a utility function to get "next" OID + // create utility function to get next oid - perhaps have a state in the control_protocol_state for a monotonically increasing oid + // + // + // Get member descriptor properties + auto role = nmos::fields::nc::role(restore_member); + auto oid = nmos::fields::nc::oid(restore_member); + auto owner = nmos::fields::nc::owner(restore_member); + auto constant_oid = nmos::fields::nc::constant_oid(restore_member); + const auto& block_member_description = nmos::fields::nc::description(restore_member); + const auto& block_member_user_label = nmos::fields::nc::user_label(restore_member); + + auto block_member_notices = web::json::value::array(); + + const auto& oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_oid_property_id); + if (oid_property_holder == web::json::value::null()) + { + auto status_message = U("Cannot find OID object property value holder"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + + continue; + } + // The values in the block member Object Property Holder will take precidence over the block member descriptor values + // If the block member values are inconsistant then warn - description and user label are independant + const auto& role_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_role_property_id); + role = role_property_holder == web::json::value::null() ? role : nmos::fields::nc::value(role_property_holder).as_string(); + + // JRT TODO: add context to error messages i.e. indicate which object has warnings + if (role_property_holder != web::json::value::null() && role != nmos::fields::nc::value(role_property_holder).as_string()) + { + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Role value in block member inconsistent with value in property holder. Property holder value takes precidence.")); + web::json::push_back(block_notices, notice); + } + if (owner != block_oid) + { + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Owner value in block member inconsistent with oid of Block. Block oid takes precidence.")); + web::json::push_back(block_notices, notice); + } + const auto& owner_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_owner_property_id); + if (owner_property_holder != web::json::value::null() && block_oid != nmos::fields::nc::value(owner_property_holder).as_integer()) + { + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Owner value in block property holder inconsistent with oid of Block. Block oid takes precidence.")); + web::json::push_back(block_member_notices, notice); + } + const auto& constant_oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_constant_oid_property_id); + constant_oid = constant_oid_property_holder == web::json::value::null() ? constant_oid : nmos::fields::nc::value(constant_oid_property_holder).as_bool(); + + if (constant_oid_property_holder != web::json::value::null() && constant_oid != nmos::fields::nc::value(constant_oid_property_holder).as_bool()) + { + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Constant OID value in block member inconsistent with value in property holder. Property holder value takes precidence.")); + web::json::push_back(block_notices, notice); + } + + const auto& user_label_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_user_label_property_id); + const auto user_label = (user_label_property_holder == web::json::value::null()) ? U("") : nmos::fields::nc::value(user_label_property_holder).as_string(); + const auto& oid2 = nmos::fields::nc::value(oid_property_holder).as_integer(); + + auto object_properties_set_validation = add_device_model_object(child_object_properties_holder, oid2, owner, role, user_label, validate); + // JRT TODO: append block_member_notices to the validation + + if (nmos::fields::nc::status(object_properties_set_validation) == nmos::nc_restore_validation_status::ok && !validate) + { + auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(block_member_description, role, oid2, constant_oid, nmos::nc_receiver_monitor_class_id, block_member_user_label, owner); + members_to_add.push_back(block_member_descriptor); + } + + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + } + else + { + // If this member had a corresponding child object properties holder then update + const auto& child_holders = nmos::get_object_properties_holder(object_properties_holders, child_role_path.as_array()); + if (child_holders.size()) + { + // JRT TODO: any changed to child objects need to be applied here - can't we delegate to a helper to do this? + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + } + } + } + auto block_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, block_notices.as_array()); + web::json::push_back(object_properties_set_validations, block_set_validation); + + // Update the members of the receivers block + if (members_to_remove.size() > 0 || members_to_add.size() > 0) + { + auto modified_members = web::json::value::array(); + + for (const auto& member : reference_members) + { + const auto& remove_member = boost::copy_range>(members_to_remove | boost::adaptors::filtered([&member](int oid) + { + return oid == nmos::fields::nc::oid(member); + }) + ); + + if (remove_member.size() == 0) + { + web::json::push_back(modified_members, member); + } + } + for (const auto& member : members_to_add) + { + web::json::push_back(modified_members, member); + } + + nmos::nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) + { + resource.data[nmos::fields::nc::members] = modified_members; + + }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { { nmos::nc_block_members_property_id, nmos::nc_property_change_type::type::value_changed, modified_members } })); + } + + return object_properties_set_validations; + } } // Check to see if root_role_path is root of role_path @@ -165,7 +403,7 @@ namespace nmos return web::json::value_from_elements(child_object_properties_holders).as_array(); } - web::json::value modify_device_model(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value modify_device_model(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) { auto object_properties_set_validation_values = web::json::value::array(); @@ -194,10 +432,10 @@ namespace nmos // if rebuildable and the block has changed then callback if (nmos::fields::nc::is_rebuildable(resource.data) && target_object_properties_holders.size() && is_block_modified(resource, *target_object_properties_holders.begin())) { - if (modify_rebuildable_block) + if (remove_device_model_object && add_device_model_object) { // call back to application code which will return an object_properties_set_validation_values object - return modify_rebuildable_block(resource, target_role_path, child_object_properties_holders, recurse, validate, get_control_protocol_class_descriptor); + return details::modify_rebuildable_block(resources, resource, target_role_path, child_object_properties_holders, recurse, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object); } else { @@ -222,7 +460,7 @@ namespace nmos auto child_role_path = web::json::value_from_elements(target_role_path); web::json::push_back(child_role_path, nmos::fields::nc::role(child->data)); - web::json::value child_object_properties_set_validation_values = modify_device_model(resources, *child, child_role_path.as_array(), child_object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, *child, child_role_path.as_array(), child_object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); // Hmmm, there must be a better way of merging two json array objects for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) { @@ -320,7 +558,7 @@ namespace nmos return role_path.as_array(); } - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) { auto object_properties_set_validation_values = web::json::value::array(); @@ -340,7 +578,7 @@ namespace nmos web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); } - web::json::value child_object_properties_set_validation_values = modify_device_model(resources, resource, target_role_path, object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + web::json::value child_object_properties_set_validation_values = modify_device_model(resources, resource, target_role_path, object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); // Hmmm - there must be a better way to append an array for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) { diff --git a/Development/nmos/configuration_utils.h b/Development/nmos/configuration_utils.h index 5bf6fa8e6..1ead1f953 100644 --- a/Development/nmos/configuration_utils.h +++ b/Development/nmos/configuration_utils.h @@ -23,7 +23,7 @@ namespace nmos // Get object_properties_holder for specified target_role_path and all its child object_properties_holders web::json::array get_child_object_properties_holders(const web::json::array& object_properties_holders, const web::json::array& target_role_path); - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block); + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); web::json::value get_property_value_holder(const web::json::value& object_properties_holder, const nmos::nc_property_id& property_id); } diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 9e462955f..99df2dcd2 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -189,9 +189,9 @@ namespace nmos return nmos::get_properties_by_path(resources, resource, recurse, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); }; } - nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block) + nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, filter_property_value_holders_handler filter_property_value_holders, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) { - return [&get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, filter_property_value_holders, modify_rebuildable_block](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [&get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -203,9 +203,9 @@ namespace nmos } auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); - if (filter_property_value_holders && modify_rebuildable_block) + if (filter_property_value_holders && remove_device_model_object && add_device_model_object) { - result = validate_set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + result = validate_set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -216,9 +216,9 @@ namespace nmos return result; }; } - nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block) + nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, filter_property_value_holders_handler filter_property_value_holders, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) { - return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, filter_property_value_holders, modify_rebuildable_block](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -230,9 +230,9 @@ namespace nmos } auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("callbacks not implemented")); - if (filter_property_value_holders && modify_rebuildable_block) + if (filter_property_value_holders && remove_device_model_object && add_device_model_object) { - result = set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + result = set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -245,7 +245,7 @@ namespace nmos } } - control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, filter_property_value_holders_handler filter_property_value_holders, modify_rebuildable_block_handler modify_rebuildable_block) + control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, filter_property_value_holders_handler filter_property_value_holders, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) { auto to_vector = [](const web::json::value& data) { @@ -384,8 +384,8 @@ namespace nmos to_methods_vector(make_nc_bulk_properties_manager_methods(), { { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this))}, - { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), filter_property_value_holders, modify_rebuildable_block) }, - { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), filter_property_value_holders, modify_rebuildable_block) } + { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), filter_property_value_holders, remove_device_model_object, add_device_model_object) }, + { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), filter_property_value_holders, remove_device_model_object, add_device_model_object) } }), to_vector(make_nc_bulk_properties_manager_events())) } }; diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index 658336932..29b0dab2b 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -59,7 +59,7 @@ namespace nmos nmos::read_lock read_lock() const { return nmos::read_lock{ mutex }; } nmos::write_lock write_lock() const { return nmos::write_lock{ mutex }; } - control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, filter_property_value_holders_handler filter_property_value_holders = nullptr, modify_rebuildable_block_handler modify_rebuildable_block = nullptr); + control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, filter_property_value_holders_handler filter_property_value_holders = nullptr, remove_device_model_object_handler remove_device_model_object = nullptr, add_device_model_object_handler add_device_model_object = nullptr); // insert control class descriptor, false if class descriptor already inserted bool insert(const experimental::control_class_descriptor& control_class_descriptor); // erase control class of the given class id, false if the required class not found diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index eba159d33..d8cd1642d 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -77,7 +77,7 @@ namespace nmos // Configure the Configuration API - node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.filter_property_value_holders, node_implementation.modify_rebuildable_block, node_implementation.control_protocol_property_changed, gate)); + node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.filter_property_value_holders, node_implementation.remove_device_model_object, node_implementation.add_device_model_object, node_implementation.control_protocol_property_changed, gate)); const auto& events_ws_port = nmos::fields::events_ws_port(node_model.settings); auto& events_ws_api = node_server.ws_handlers[{ {}, nmos::fields::events_ws_port(node_model.settings) }]; diff --git a/Development/nmos/node_server.h b/Development/nmos/node_server.h index b4e7e9106..30d764cb5 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -28,7 +28,7 @@ namespace nmos // underlying implementation into the server instance for the NMOS Node struct node_implementation { - node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::modify_rebuildable_block_handler modify_rebuildable_block) + node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::filter_property_value_holders_handler filter_property_value_holders, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) : load_server_certificates(std::move(load_server_certificates)) , load_dh_param(std::move(load_dh_param)) , load_ca_certificates(std::move(load_ca_certificates)) @@ -52,7 +52,8 @@ namespace nmos , get_control_protocol_method_descriptor(std::move(get_control_protocol_method_descriptor)) , control_protocol_property_changed(std::move(control_protocol_property_changed)) , filter_property_value_holders(std::move(filter_property_value_holders)) - , modify_rebuildable_block(std::move(modify_rebuildable_block)) + , remove_device_model_object(std::move(remove_device_model_object)) + , add_device_model_object(std::move(add_device_model_object)) {} // use the default constructor and chaining member functions for fluent initialization @@ -86,7 +87,8 @@ namespace nmos node_implementation& on_get_control_protocol_method_descriptor(nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor) { this->get_control_protocol_method_descriptor = std::move(get_control_protocol_method_descriptor); return *this; } node_implementation& on_control_protocol_property_changed(nmos::control_protocol_property_changed_handler control_protocol_property_changed) { this->control_protocol_property_changed = std::move(control_protocol_property_changed); return *this; } node_implementation& on_filter_property_value_holders(nmos::filter_property_value_holders_handler filter_property_value_holders) { this->filter_property_value_holders = std::move(filter_property_value_holders); return *this; } - node_implementation& on_modify_rebuildable_block(nmos::modify_rebuildable_block_handler modify_rebuildable_block) { this->modify_rebuildable_block = std::move(modify_rebuildable_block); return *this; } + node_implementation& on_remove_device_model_object(nmos::remove_device_model_object_handler remove_device_model_object) { this->remove_device_model_object = std::move(remove_device_model_object); return *this; } + node_implementation& on_add_device_model_object(nmos::add_device_model_object_handler add_device_model_object) { this->add_device_model_object = std::move(add_device_model_object); return *this; } // deprecated, use on_validate_connection_resource_patch node_implementation& on_validate_merged(nmos::details::connection_resource_patch_validator validate_merged) { return on_validate_connection_resource_patch(std::move(validate_merged)); } @@ -132,7 +134,8 @@ namespace nmos // Device Configuration handlers nmos::filter_property_value_holders_handler filter_property_value_holders; - nmos::modify_rebuildable_block_handler modify_rebuildable_block; + nmos::remove_device_model_object_handler remove_device_model_object; + nmos::add_device_model_object_handler add_device_model_object; }; // Construct a server instance for an NMOS Node, implementing the IS-04 Node API, IS-05 Connection API, IS-07 Events API diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 10b3d7aac..468bfcb62 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -429,6 +429,7 @@ BST_TEST_CASE(testApplyBackupDataSet) auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor_2_oid = oid; nmos::nc::push_back(receivers, monitor1); // add example-control to root-block nmos::nc::push_back(receivers, monitor2); @@ -443,28 +444,35 @@ BST_TEST_CASE(testApplyBackupDataSet) insert_resource(resources, std::move(monitor2)); bool filter_property_value_holders_called = false; - bool modify_rebuildable_block_called = false; + bool remove_device_model_object_called = false; + bool add_device_model_object_called = false; // callback stubs nmos::filter_property_value_holders_handler filter_property_value_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + { + filter_property_value_holders_called = true; + auto modifiable_property_value_holders = value::array(); + + for (const auto& property_value : property_values) { - filter_property_value_holders_called = true; - auto modifiable_property_value_holders = value::array(); - - for (const auto& property_value : property_values) - { - web::json::push_back(modifiable_property_value_holders, property_value); - } - return modifiable_property_value_holders.as_array(); - }; - nmos::modify_rebuildable_block_handler modify_rebuildable_block = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) - { - modify_rebuildable_block_called = true; - auto out = value::array(); - const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, U("OK")); - web::json::push_back(out, object_properties_set_validation); - return out; - }; + web::json::push_back(modifiable_property_value_holders, property_value); + } + return modifiable_property_value_holders.as_array(); + }; + + nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::nc_oid reference_oid, bool validate) + { + remove_device_model_object_called = true; + + return true; + }; + + nmos::add_device_model_object_handler add_device_model_object = [&](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate) + { + add_device_model_object_called = true; + const auto& role_path = nmos::fields::nc::path(object_properties_holder); + return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::ok, U("OK")); + }; { // Check the successful modification of the "enabled" flag of mon1's worker base class in Modify mode @@ -483,7 +491,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -495,13 +503,15 @@ BST_TEST_CASE(testApplyBackupDataSet) // not expecting callbacks to be invoked as no read only properties, or rebuildable blocks modified BST_CHECK(!filter_property_value_holders_called); - BST_CHECK(!modify_rebuildable_block_called); + BST_CHECK(!remove_device_model_object_called); + BST_CHECK(!add_device_model_object_called); } { // Check filter_property_value_holders_handler is called when changing a read only property of rebuildable object in Rebuild mode // filter_property_value_holders_called = false; - modify_rebuildable_block_called = false; + remove_device_model_object_called = false; + add_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -520,7 +530,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -533,13 +543,15 @@ BST_TEST_CASE(testApplyBackupDataSet) // expecting callback to filter_property_value_holders_called // but not to modify_rebuildable_block_called BST_CHECK(filter_property_value_holders_called); - BST_CHECK(!modify_rebuildable_block_called); + BST_CHECK(!remove_device_model_object_called); + BST_CHECK(!add_device_model_object_called); } { // Check error generated when attempting to change a read only property of non-rebuidable object in Rebuild mode // filter_property_value_holders_called = false; - modify_rebuildable_block_called = false; + remove_device_model_object_called = false; + add_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -558,7 +570,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -581,13 +593,15 @@ BST_TEST_CASE(testApplyBackupDataSet) // expecting callback to filter_property_value_holders_called // but not to modify_rebuildable_block_called BST_CHECK(!filter_property_value_holders_called); - BST_CHECK(!modify_rebuildable_block_called); + BST_CHECK(!remove_device_model_object_called); + BST_CHECK(!add_device_model_object_called); } { // Check an error is caused by trying to modify a read only property in Modify mode // filter_property_value_holders_called = false; - modify_rebuildable_block_called = false; + remove_device_model_object_called = false; + add_device_model_object_called = false; // Change a read only property in Rebuild mode // Create Object Properties Holder @@ -608,7 +622,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -630,21 +644,23 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); BST_CHECK(!filter_property_value_holders_called); - BST_CHECK(!modify_rebuildable_block_called); + BST_CHECK(!remove_device_model_object_called); + BST_CHECK(!add_device_model_object_called); } { - // Check modify_rebuildable_block_handler is called when trying to modify a rebuildable block + // Check remove_device_model_object_called is called when trying to modify a rebuildable block // filter_property_value_holders_called = false; - modify_rebuildable_block_called = false; + remove_device_model_object_called = false; + add_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); - const auto role_path = value_of({ U("root"), U("receivers")}); + const auto role_path = value_of({ U("root"), U("receivers") }); auto property_value_holders = value::array(); auto members = value::array(); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -653,7 +669,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -664,13 +680,99 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); BST_CHECK(!filter_property_value_holders_called); - BST_CHECK(modify_rebuildable_block_called); + BST_CHECK(remove_device_model_object_called); + BST_CHECK(!add_device_model_object_called); + } + { + // Check add_device_model_object_called is called when trying to modify a rebuildable block + // + filter_property_value_holders_called = false; + remove_device_model_object_called = false; + add_device_model_object_called = false; + + auto monitor_3_oid = 999; + // Create Object Properties Holder for Block, with a Property Holder for the block members + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers") }); + { + auto property_value_holders = value::array(); + auto members = value::array(); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + // Create Object Properties Holder for new monitor + const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + { + auto property_value_holders = value::array(); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + { + auto property_value_holders = value::array(); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); + { + auto property_value_holders = value::array(); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + bool validate = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + + // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors + BST_REQUIRE_EQUAL(4, output.as_array().size()); + + // Check the correct object properties holders have been returned + const auto& block_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), role_path.as_array()); + BST_REQUIRE_EQUAL(block_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = block_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + } + const auto& monitor_1_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_1_role_path.as_array()); + BST_REQUIRE_EQUAL(monitor_1_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = monitor_1_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + } + const auto& monitor_2_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_2_role_path.as_array()); + BST_REQUIRE_EQUAL(monitor_2_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = monitor_2_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + } + const auto& monitor_3_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_3_role_path.as_array()); + BST_REQUIRE_EQUAL(monitor_3_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = monitor_3_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + } + + BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!remove_device_model_object_called); + BST_CHECK(add_device_model_object_called); } { // Check that role paths outside of the scope of the target role path are errored // filter_property_value_holders_called = false; - modify_rebuildable_block_called = false; + remove_device_model_object_called = false; + add_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -678,7 +780,7 @@ BST_TEST_CASE(testApplyBackupDataSet) auto property_value_holders = value::array(); auto members = value::array(); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -687,7 +789,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -698,13 +800,15 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); BST_CHECK(!filter_property_value_holders_called); - BST_CHECK(!modify_rebuildable_block_called); + BST_CHECK(!remove_device_model_object_called); + BST_CHECK(!add_device_model_object_called); } { // Mixture of filter_property_value_holders_handler and errors in Rebuild mode // filter_property_value_holders_called = false; - modify_rebuildable_block_called = false; + remove_device_model_object_called = false; + add_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -722,7 +826,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -744,13 +848,15 @@ BST_TEST_CASE(testApplyBackupDataSet) // expecting callback to filter_property_value_holders_called // but not to modify_rebuildable_block_called BST_CHECK(filter_property_value_holders_called); - BST_CHECK(!modify_rebuildable_block_called); + BST_CHECK(!remove_device_model_object_called); + BST_CHECK(!add_device_model_object_called); } { // Incorrect property name in property value holders // filter_property_value_holders_called = false; - modify_rebuildable_block_called = false; + remove_device_model_object_called = false; + add_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -766,7 +872,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -788,13 +894,15 @@ BST_TEST_CASE(testApplyBackupDataSet) // expecting callback to filter_property_value_holders_called // but not to modify_rebuildable_block_called BST_CHECK(!filter_property_value_holders_called); - BST_CHECK(!modify_rebuildable_block_called); + BST_CHECK(!remove_device_model_object_called); + BST_CHECK(!add_device_model_object_called); } { // Incorrect property type in property value holders // filter_property_value_holders_called = false; - modify_rebuildable_block_called = false; + remove_device_model_object_called = false; + add_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -810,7 +918,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -832,7 +940,8 @@ BST_TEST_CASE(testApplyBackupDataSet) // expecting callback to filter_property_value_holders_called // but not to modify_rebuildable_block_called BST_CHECK(!filter_property_value_holders_called); - BST_CHECK(!modify_rebuildable_block_called); + BST_CHECK(!remove_device_model_object_called); + BST_CHECK(!add_device_model_object_called); } // ensure an error if trying to invoke rebuildable block when in Modify mode } @@ -879,7 +988,8 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) // undefined callback stubs nmos::filter_property_value_holders_handler filter_property_value_holders; - nmos::modify_rebuildable_block_handler modify_rebuildable_block; + nmos::remove_device_model_object_handler remove_device_model_object; + nmos::add_device_model_object_handler add_device_model_object; { // Check that Modify mode is unaffected by undefined Rebuild mode callbacks @@ -898,7 +1008,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -925,7 +1035,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -955,7 +1065,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -983,7 +1093,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, modify_rebuildable_block); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -992,4 +1102,206 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); } -} \ No newline at end of file +} + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) +{ + using web::json::value_of; + using web::json::value; + + nmos::resources resources; + nmos::experimental::control_protocol_state control_protocol_state; + nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor = nmos::make_get_control_protocol_class_descriptor_handler(control_protocol_state); + + // Create Device Model + // root + auto root_block = nmos::make_root_block(); + auto oid = nmos::root_block_oid; + // root, ClassManager + auto class_manager = nmos::make_class_manager(++oid, control_protocol_state); + auto receiver_block_oid = ++oid; + // root, receivers + auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); + nmos::make_rebuildable(receivers); + // root, receivers, mon1 + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + // make monitor1 rebuildable + nmos::make_rebuildable(monitor1); + + auto monitor_1_oid = oid; + auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + // root, receivers, mon2 + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor_2_oid = oid; + nmos::nc::push_back(receivers, monitor1); + // add example-control to root-block + nmos::nc::push_back(receivers, monitor2); + // add stereo-gain to root-block + nmos::nc::push_back(root_block, receivers); + // add class-manager to root-block + nmos::nc::push_back(root_block, class_manager); + insert_resource(resources, std::move(root_block)); + insert_resource(resources, std::move(class_manager)); + insert_resource(resources, std::move(receivers)); + insert_resource(resources, std::move(monitor1)); + insert_resource(resources, std::move(monitor2)); + + bool filter_property_value_holders_called = false; + bool remove_device_model_object_called = false; + bool add_device_model_object_called = false; + + // callback stubs + nmos::filter_property_value_holders_handler filter_property_value_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + { + filter_property_value_holders_called = true; + auto modifiable_property_value_holders = value::array(); + + for (const auto& property_value : property_values) + { + web::json::push_back(modifiable_property_value_holders, property_value); + } + return modifiable_property_value_holders.as_array(); + }; + + nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::nc_oid reference_oid, bool validate) + { + remove_device_model_object_called = true; + + // Simulate error on removing object from device model + return false; + }; + + nmos::add_device_model_object_handler add_device_model_object = [&](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate) + { + add_device_model_object_called = true; + const auto& role_path = nmos::fields::nc::path(object_properties_holder); + // Simulate error on adding object to device model + return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::device_error, U("Unable to add object to device model")); + }; + { + // Check remove_device_model_object_called error is handled when attempting to modify a rebuildable block + // + filter_property_value_holders_called = false; + remove_device_model_object_called = false; + add_device_model_object_called = false; + + // Create Object Properties Holder + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers") }); + auto property_value_holders = value::array(); + auto members = value::array(); + + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + bool validate = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + + // expectation is there will be a result for each of the object_properties_holders i.e. one + BST_REQUIRE_EQUAL(1, output.as_array().size()); + const auto& object_properties_set_validation = output.as_array().at(0); + + BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::device_error, nmos::fields::nc::status(object_properties_set_validation)); + BST_REQUIRE_EQUAL(1, nmos::fields::nc::notices(object_properties_set_validation).size()); + const auto& notice = nmos::fields::nc::notices(object_properties_set_validation).at(0); + BST_CHECK_EQUAL(nmos::fields::nc::notice_type(notice), nmos::nc_property_restore_notice_type::error); + const auto& property_id = nmos::fields::nc::id(notice); + BST_CHECK_EQUAL(nmos::fields::nc::level(property_id), nmos::nc_block_members_property_id.level); + BST_CHECK_EQUAL(nmos::fields::nc::index(property_id), nmos::nc_block_members_property_id.index); + + BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(remove_device_model_object_called); + BST_CHECK(!add_device_model_object_called); + } + { + // Check add_device_model_object_called error is handlerd when trying to modify a rebuildable block + // + filter_property_value_holders_called = false; + remove_device_model_object_called = false; + add_device_model_object_called = false; + + auto monitor_3_oid = 999; + // Create Object Properties Holder for Block, with a Property Holder for the block members + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers") }); + { + auto property_value_holders = value::array(); + auto members = value::array(); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + // Create Object Properties Holder for new monitor + const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + { + auto property_value_holders = value::array(); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + { + auto property_value_holders = value::array(); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); + { + auto property_value_holders = value::array(); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + bool validate = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + + // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors + BST_REQUIRE_EQUAL(4, output.as_array().size()); + + // Check the correct object properties holders have been returned + const auto& block_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), role_path.as_array()); + BST_REQUIRE_EQUAL(block_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = block_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + } + const auto& monitor_1_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_1_role_path.as_array()); + BST_REQUIRE_EQUAL(monitor_1_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = monitor_1_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + } + const auto& monitor_2_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_2_role_path.as_array()); + BST_REQUIRE_EQUAL(monitor_2_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = monitor_2_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + } + const auto& monitor_3_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_3_role_path.as_array()); + BST_REQUIRE_EQUAL(monitor_3_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = monitor_3_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::device_error, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + } + + BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!remove_device_model_object_called); + BST_CHECK(add_device_model_object_called); + } +} From 5025f5a21e6c868c892c85f83832bebf47e53966 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Fri, 20 Jun 2025 14:31:28 +0100 Subject: [PATCH 189/250] Refactor and handle more error conditions --- Development/nmos/configuration_utils.cpp | 305 ++++++++------ .../nmos/test/configuration_utils_test.cpp | 387 ++++++++++++++++-- 2 files changed, 541 insertions(+), 151 deletions(-) diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 6c525c972..0e34855da 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -72,43 +72,74 @@ namespace nmos return false; } - web::json::value modify_rebuildable_block(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) + web::json::value modify_device_model_object(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& target_object_properties_holder, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders) { - // rebuildable block and child objects are passed to this function for modification - auto object_properties_set_validations = web::json::value::array(); + const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); - // Find object_properties_holder for resource - const auto& filtered_holders = nmos::get_object_properties_holder(object_properties_holders, nmos::get_role_path(resources, resource)); + auto object_properties_set_validation_values = web::json::value::array(); - if (filtered_holders.size() != 1) - { - auto status_message = U("Either can't find associated object_properties_holder, or there's more than one (ambiguous)"); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); + auto property_restore_notices = web::json::value::array(); + // Validate property_values - filter out the incorrect, ignored or unallowed values + // notices are created for any properties that can't be modified according to restore mode + // and whether this object is rebuildable + const auto& filtered_property_values = boost::copy_range>(nmos::fields::nc::values(target_object_properties_holder) + | boost::adaptors::filtered([&property_restore_notices, &resource, class_id, get_control_protocol_class_descriptor, restore_mode](const web::json::value& property_value) + { + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); - return object_properties_set_validations; - } + return resource.data.at(nmos::fields::nc::name(property_descriptor)) != nmos::fields::nc::value(property_value) + && details::is_property_value_valid(property_restore_notices, property_value, property_descriptor, restore_mode, bool(nmos::fields::nc::is_rebuildable(resource.data))); + }) + ); + auto property_modify_list = web::json::value_from_elements(filtered_property_values).as_array(); - if (!nmos::nc::is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)))) + if (details::is_contains_read_only_property(property_modify_list, class_id, get_control_protocol_class_descriptor)) { - auto status_message = U("Expected an NcBlock"); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); - - return object_properties_set_validations; + if (filter_property_value_holders) + { + // If the property_modify_list contains read only properties then we call back to the application code to + // check that it's OK to change those value. Bear in mind that these could be the class Id, or the oid or some other + // property that we don't want changed ordinarily + property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list, recurse, validate, property_restore_notices.as_array(), get_control_protocol_class_descriptor); + } + else + { + // Modify of read only properties not supported + return nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, property_restore_notices.as_array(), U("Modification of read only properties not supported")); + } } + for (const auto& property_value : property_modify_list) + { + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); - const auto& block_members_properties_holder = nmos::get_property_value_holder(*filtered_holders.begin(), nmos::nc_block_members_property_id); + // hmmm, ideally we would pass the value into modify_resource with the validate + // flag, so that it's subject to property contraints and also the application code can decide if it's a legal value + if (!validate) + { + // modify control protocol resources + const auto& value = nmos::fields::nc::value(property_value); - if (block_members_properties_holder.is_null()) - { - // JRT TODO: just update the block properties in this case - auto status_message = U("No block members properties holder found"); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, status_message); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); + nc::modify_resource(resources, resource.id, [&](nmos::resource& resource_) + { + resource_.data[nmos::fields::nc::name(property_value)] = value; - return object_properties_set_validations; + }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { {property_id, nmos::nc_property_change_type::type::value_changed, value} })); + } } + return nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, property_restore_notices.as_array(), U("OK")); + } + + web::json::value modify_rebuildable_block(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, nmos::filter_property_value_holders_handler filter_property_value_holders) + { + // rebuildable block and child objects are passed to this function for modification + auto object_properties_set_validations = web::json::value::array(); + + // Find object_properties_holder for resource + // the parent function guarantees that this target role path is in the object_properties_holders + // and property_holder for block members exists + const auto& filtered_holders = nmos::get_object_properties_holder(object_properties_holders, target_role_path); + const auto& block_members_properties_holder = nmos::get_property_value_holder(*filtered_holders.begin(), nmos::nc_block_members_property_id); const auto& restore_members = nmos::fields::nc::value(block_members_properties_holder); const auto& reference_members = nmos::fields::nc::members(resource.data); @@ -118,6 +149,9 @@ namespace nmos std::vector members_to_remove; std::vector members_to_add; + // If there are any data problems they should be reported as warning/error notices + auto block_notices = web::json::value::array(); + // Iterate through the members of the block and compare to the members in the backup dataset for (const auto& reference_member : reference_members) { @@ -133,7 +167,6 @@ namespace nmos if (filtered_members.size() != 1) { // can't find this role in restore dataset, so member has been removed - // get the receiver monitor resource bool success = remove_device_model_object(nmos::fields::nc::oid(reference_member), validate); if (success) { @@ -144,19 +177,15 @@ namespace nmos } else { - // unable to delete resource so stop updating block and report the error + // unable to delete resource so report the error and don't update block auto notices = web::json::value::array(); const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Unable to delete resource from Device Model.")); - web::json::push_back(notices, notice); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::device_error, notices.as_array(), U("Unable to delete resource from Device Model")); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); + web::json::push_back(block_notices, notice); - return object_properties_set_validations; + continue; } } } - // If there are any data problems they should be reported as warning/error notices - auto block_notices = web::json::value::array(); for (const auto& restore_member : restore_members.as_array()) { @@ -171,9 +200,8 @@ namespace nmos ); if (filtered_members.size() != 1) { - // can't find this role in existing members, so member has been added - // Add this resource - // Find the object_properties_holder that describes the receiver monitor + // can't find this role in existing members, so member need to be added + // Find the object_properties_holder that describes the receiver monitor object const auto& filtered_child_object_properties_holders = boost::copy_range>(object_properties_holders | boost::adaptors::filtered([&child_role_path](const web::json::value& object_properties_holder) { @@ -192,70 +220,110 @@ namespace nmos const auto& child_object_properties_holder = *filtered_child_object_properties_holders.begin(); - // JRT TODO: unsafe to take oid from backup dataset - need to create a new OID with no clash with existing OIDs so need a utility function to get "next" OID - // create utility function to get next oid - perhaps have a state in the control_protocol_state for a monotonically increasing oid - // - // // Get member descriptor properties auto role = nmos::fields::nc::role(restore_member); - auto oid = nmos::fields::nc::oid(restore_member); auto owner = nmos::fields::nc::owner(restore_member); auto constant_oid = nmos::fields::nc::constant_oid(restore_member); + auto oid = nmos::fields::nc::oid(restore_member); const auto& block_member_description = nmos::fields::nc::description(restore_member); const auto& block_member_user_label = nmos::fields::nc::user_label(restore_member); - auto block_member_notices = web::json::value::array(); + auto added_object_notices = web::json::value::array(); const auto& oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_oid_property_id); - if (oid_property_holder == web::json::value::null()) + oid = oid_property_holder == web::json::value::null() ? oid : nmos::fields::nc::value(oid_property_holder).as_integer(); + + if (oid_property_holder != web::json::value::null() && oid != nmos::fields::nc::value(oid_property_holder).as_integer()) { - auto status_message = U("Cannot find OID object property value holder"); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("OID value in block member inconsistent with value in property holder. Property holder value takes precidence.")); + web::json::push_back(block_notices, notice); + } - continue; + if (constant_oid) + { + // If constant oid then check oid from property holder and verify with oid from block member + const auto& child = find_resource(resources, utility::s2us(std::to_string(oid))); + + if (resources.end() != child) + { + // oid already in use! + // create device error for new object + auto status_message = U("Constant OID error. OID already in use"); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::device_error, status_message); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + // also create error notice for the block + const auto block_notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, status_message); + web::json::push_back(block_notices, block_notice); + continue; + } } + else + { + // Ignore specified OID and generate new one + int max_oid = -1; + for (const auto& r: resources) + { + max_oid = std::max(max_oid, nmos::fields::nc::oid(r.data)); + } + oid = ++max_oid; + const auto block_notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Dynamically generating new OID for new block member.")); + web::json::push_back(block_notices, block_notice); + + const auto added_object_notice = nmos::details::make_nc_property_restore_notice(nmos::nc_object_oid_property_id, U("oid"), nmos::nc_property_restore_notice_type::warning, U("Dynamically generating new OID for new object.")); + web::json::push_back(added_object_notices, added_object_notice); + } + // The values in the block member Object Property Holder will take precidence over the block member descriptor values // If the block member values are inconsistant then warn - description and user label are independant const auto& role_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_role_property_id); role = role_property_holder == web::json::value::null() ? role : nmos::fields::nc::value(role_property_holder).as_string(); - // JRT TODO: add context to error messages i.e. indicate which object has warnings if (role_property_holder != web::json::value::null() && role != nmos::fields::nc::value(role_property_holder).as_string()) { - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Role value in block member inconsistent with value in property holder. Property holder value takes precidence.")); + utility::stringstream_t ss; + ss << U("Role value in block member inconsistent with value in property holder. Property holder value takes precidence for role=") << role; + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); web::json::push_back(block_notices, notice); } if (owner != block_oid) { - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Owner value in block member inconsistent with oid of Block. Block oid takes precidence.")); + utility::stringstream_t ss; + ss << U("Owner value in block member inconsistent with oid of Block. Block oid takes precidence for role=") << role; + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); web::json::push_back(block_notices, notice); } const auto& owner_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_owner_property_id); if (owner_property_holder != web::json::value::null() && block_oid != nmos::fields::nc::value(owner_property_holder).as_integer()) { - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Owner value in block property holder inconsistent with oid of Block. Block oid takes precidence.")); - web::json::push_back(block_member_notices, notice); + utility::stringstream_t ss; + ss << U("Owner value in block property holder inconsistent with oid of Block. Block oid takes precidence for role=") << role; + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); + web::json::push_back(added_object_notices, notice); } const auto& constant_oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_constant_oid_property_id); constant_oid = constant_oid_property_holder == web::json::value::null() ? constant_oid : nmos::fields::nc::value(constant_oid_property_holder).as_bool(); if (constant_oid_property_holder != web::json::value::null() && constant_oid != nmos::fields::nc::value(constant_oid_property_holder).as_bool()) { - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Constant OID value in block member inconsistent with value in property holder. Property holder value takes precidence.")); + utility::stringstream_t ss; + ss << U("Constant OID value in block property holder inconsistent with oid of Block. Block oid takes precidence for role=") << role; + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); web::json::push_back(block_notices, notice); } const auto& user_label_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_user_label_property_id); - const auto user_label = (user_label_property_holder == web::json::value::null()) ? U("") : nmos::fields::nc::value(user_label_property_holder).as_string(); - const auto& oid2 = nmos::fields::nc::value(oid_property_holder).as_integer(); + const auto& user_label = (user_label_property_holder == web::json::value::null()) ? block_member_user_label : nmos::fields::nc::value(user_label_property_holder).as_string(); - auto object_properties_set_validation = add_device_model_object(child_object_properties_holder, oid2, owner, role, user_label, validate); - // JRT TODO: append block_member_notices to the validation + auto object_properties_set_validation = add_device_model_object(child_object_properties_holder, oid, owner, role, user_label, validate); + // Add warnings about known inconsistancies between backup dataset and new device model object + for (const auto& added_object_notice: added_object_notices.as_array()) + { + web::json::push_back(nmos::fields::nc::notices(object_properties_set_validation), added_object_notice); + } if (nmos::fields::nc::status(object_properties_set_validation) == nmos::nc_restore_validation_status::ok && !validate) { - auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(block_member_description, role, oid2, constant_oid, nmos::nc_receiver_monitor_class_id, block_member_user_label, owner); + auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(block_member_description, role, oid, constant_oid, nmos::nc_receiver_monitor_class_id, block_member_user_label, owner); members_to_add.push_back(block_member_descriptor); } @@ -264,16 +332,24 @@ namespace nmos else { // If this member had a corresponding child object properties holder then update - const auto& child_holders = nmos::get_object_properties_holder(object_properties_holders, child_role_path.as_array()); - if (child_holders.size()) + const auto& child_object_properties_holder = nmos::get_object_properties_holder(object_properties_holders, child_role_path.as_array()); + if (child_object_properties_holder.size()) { - // JRT TODO: any changed to child objects need to be applied here - can't we delegate to a helper to do this? - const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok); + const auto& child = find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(restore_member)))); + const auto object_properties_set_validation = details::modify_device_model_object(resources, *child, child_role_path.as_array(), child_object_properties_holder.at(0), recurse, nmos::nc_restore_mode::rebuild, validate, get_control_protocol_class_descriptor, filter_property_value_holders); web::json::push_back(object_properties_set_validations, object_properties_set_validation); } } } - auto block_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, block_notices.as_array()); + // If there are any error notices, then give an overall error status for object properties set validation + const auto& error_notices = boost::copy_range>(block_notices.as_array() + | boost::adaptors::filtered([&](const web::json::value& notice) + { + return nmos::fields::nc::notice_type(notice) == nmos::nc_property_restore_notice_type::error; + }) + ); + const auto block_status = error_notices.size() ? nmos::nc_restore_validation_status::failed : nmos::nc_restore_validation_status::ok; + auto block_set_validation = nmos::make_object_properties_set_validation(target_role_path, block_status, block_notices.as_array()); web::json::push_back(object_properties_set_validations, block_set_validation); // Update the members of the receivers block @@ -283,22 +359,26 @@ namespace nmos for (const auto& member : reference_members) { + // Is this member in the members_to_remove array? const auto& remove_member = boost::copy_range>(members_to_remove | boost::adaptors::filtered([&member](int oid) { return oid == nmos::fields::nc::oid(member); }) ); + // If not add it to the members array if (remove_member.size() == 0) { web::json::push_back(modified_members, member); } } + // Then add the members_to_add for (const auto& member : members_to_add) { web::json::push_back(modified_members, member); } + // Update the block in the device model nmos::nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) { resource.data[nmos::fields::nc::members] = modified_members; @@ -409,8 +489,6 @@ namespace nmos // Filter for the target_role_path and child objects // - // hmmmmm, I don't like this two step filter process - creating a boost array and then converting to a json array. - // Could this be done in a single step? const auto& child_object_properties_holders = get_child_object_properties_holders(object_properties_holders, target_role_path); // get object_properties_holder for the target role path, if there is one @@ -420,8 +498,19 @@ namespace nmos if (target_object_properties_holders.size() > 1) { // Error in the backup dataset - const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(nmos::fields::nc::path(*target_object_properties_holders.begin()), nmos::nc_restore_validation_status::failed, U("more than one object_properties_holder for role path")); - web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + // Generate errors for all object properties holders + utility::stringstream_t ss; + ss << U("duplicate object_properties_holder for role path: "); + for (const auto& element: target_role_path) + { + ss << element << " "; + } + for (const auto& object_properties_holder: object_properties_holders) + { + ss << "."; // hmmmm, object properties set validations with identical data can't be filtered, so vary error message slightly + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(nmos::fields::nc::path(object_properties_holder), nmos::nc_restore_validation_status::failed, ss.str()); + web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + } return object_properties_set_validation_values; } @@ -429,20 +518,28 @@ namespace nmos if (recurse && nmos::nc::is_block(class_id)) { - // if rebuildable and the block has changed then callback - if (nmos::fields::nc::is_rebuildable(resource.data) && target_object_properties_holders.size() && is_block_modified(resource, *target_object_properties_holders.begin())) + if (target_object_properties_holders.size()) { - if (remove_device_model_object && add_device_model_object) + if (nmos::fields::nc::is_rebuildable(resource.data) && restore_mode == nmos::nc_restore_mode::rebuild && is_block_modified(resource, *target_object_properties_holders.begin())) { - // call back to application code which will return an object_properties_set_validation_values object - return details::modify_rebuildable_block(resources, resource, target_role_path, child_object_properties_holders, recurse, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object); + // Modify rebuildable block + if (remove_device_model_object && add_device_model_object) + { + // Process this block and all children of this block + return details::modify_rebuildable_block(resources, resource, target_role_path, child_object_properties_holders, recurse, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object, filter_property_value_holders); + } + else + { + // Rebuilding blocks not supported + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, U("Rebuilding of Device Model blocks not supported")); + web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + } } else { - // Rebuilding blocks not supported - const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, U("Rebuilding of Device Model blocks not supported")); + // Modify non-rebuiladable block + const auto object_properties_set_validation = details::modify_device_model_object(resources, resource, target_role_path, target_object_properties_holders.at(0), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); - return object_properties_set_validation_values; } } // iterate through child objects @@ -470,61 +567,15 @@ namespace nmos } } } - for (const auto& target_object_properties_holder : target_object_properties_holders) + else { - auto property_restore_notices = web::json::value::array(); - // Validate property_values - filter out the incorrect, ignored or unallowed values - const auto& filtered_property_values = boost::copy_range>(nmos::fields::nc::values(target_object_properties_holder) - | boost::adaptors::filtered([&property_restore_notices, &resource, class_id, get_control_protocol_class_descriptor, restore_mode](const web::json::value& property_value) - { - const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); - const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); - - return resource.data.at(nmos::fields::nc::name(property_descriptor)) != nmos::fields::nc::value(property_value) - && details::is_property_value_valid(property_restore_notices, property_value, property_descriptor, restore_mode, bool(nmos::fields::nc::is_rebuildable(resource.data))); - }) - ); - auto property_modify_list = web::json::value_from_elements(filtered_property_values).as_array(); - - if (details::is_contains_read_only_property(property_modify_list, class_id, get_control_protocol_class_descriptor)) - { - if (filter_property_value_holders) - { - // If the property_modify_list contains read only properties then we call back to the application code to - // check that it's OK to change those value. Bear in mind that they could be the class Id, or the oid or some other - // property that we don't want changed - property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list, recurse, validate, property_restore_notices.as_array(), get_control_protocol_class_descriptor); - } - else - { - // Modify of read only properties not supported - const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, property_restore_notices.as_array(), U("Modification of read only properties not supported")); - web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); - continue; - } - } - for (const auto& property_value : property_modify_list) + if (target_object_properties_holders.size()) { - const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); - - // hmmm, ideally we would pass the value into modify_resource with the validate - // flag, so that it's subject to property contraints and also the application code can decide if it's a legal value - if (!validate) - { - // modify control protocol resources - const auto& value = nmos::fields::nc::value(property_value); - - nc::modify_resource(resources, resource.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} })); - } + // Modify object + const auto object_properties_set_validation = details::modify_device_model_object(resources, resource, target_role_path, target_object_properties_holders.at(0), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders); + web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); } - const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, property_restore_notices.as_array(), U("OK")); - web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); } - return object_properties_set_validation_values; } diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 468bfcb62..9c616dc5e 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -704,12 +704,6 @@ BST_TEST_CASE(testApplyBackupDataSet) push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor - const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - { - auto property_value_holders = value::array(); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); - } const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_value_holders = value::array(); @@ -731,7 +725,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors - BST_REQUIRE_EQUAL(4, output.as_array().size()); + BST_REQUIRE_EQUAL(3, output.as_array().size()); // Check the correct object properties holders have been returned const auto& block_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), role_path.as_array()); @@ -741,13 +735,6 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); } - const auto& monitor_1_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_1_role_path.as_array()); - BST_REQUIRE_EQUAL(monitor_1_object_properties_holder.size(), 1); - { - const auto& object_properties_set_validation = monitor_1_object_properties_holder.at(0); - BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); - BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); - } const auto& monitor_2_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_2_role_path.as_array()); BST_REQUIRE_EQUAL(monitor_2_object_properties_holder.size(), 1); { @@ -1076,7 +1063,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); } { - // Check undefined modify_rebuildable_block_handler causes an unsupported error when attempting to modify a rebuildable block + // Check undefined add_device_model_object and remove_device_model_object causes an unsupported error when attempting to modify a rebuildable block // // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -1084,9 +1071,15 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto property_value_holders = value::array(); auto members = value::array(); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + { + auto property_value_holders = value::array(); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; @@ -1095,12 +1088,247 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); - // expectation is there will be a result for each of the object_properties_holders i.e. one - BST_REQUIRE_EQUAL(1, output.as_array().size()); - const auto object_properties_set_validation = output.as_array().at(0); + // expectation is there will be a result for each of the object_properties_holder + BST_CHECK_EQUAL(2, output.as_array().size()); + const auto& block_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), role_path.as_array()); + BST_REQUIRE_EQUAL(block_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = block_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); + BST_REQUIRE_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + } + const auto& monitor_1_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_1_role_path.as_array()); + BST_REQUIRE_EQUAL(monitor_1_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = monitor_1_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + } + } +} - BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); - BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) +{ + using web::json::value_of; + using web::json::value; + + nmos::resources resources; + nmos::experimental::control_protocol_state control_protocol_state; + nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor = nmos::make_get_control_protocol_class_descriptor_handler(control_protocol_state); + + // Create Device Model + // root + auto root_block = nmos::make_root_block(); + auto oid = nmos::root_block_oid; + // root, ClassManager + auto class_manager = nmos::make_class_manager(++oid, control_protocol_state); + auto receiver_block_oid = ++oid; + // root, receivers + auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); + nmos::make_rebuildable(receivers); + // root, receivers, mon1 + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({{nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); + // make monitor1 rebuildable + nmos::make_rebuildable(monitor1); + + auto monitor_1_oid = oid; + auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + // root, receivers, mon2 + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor_2_oid = oid; + nmos::nc::push_back(receivers, monitor1); + // add example-control to root-block + nmos::nc::push_back(receivers, monitor2); + // add stereo-gain to root-block + nmos::nc::push_back(root_block, receivers); + // add class-manager to root-block + nmos::nc::push_back(root_block, class_manager); + insert_resource(resources, std::move(root_block)); + insert_resource(resources, std::move(class_manager)); + insert_resource(resources, std::move(receivers)); + insert_resource(resources, std::move(monitor1)); + insert_resource(resources, std::move(monitor2)); + + bool filter_property_value_holders_called = false; + bool remove_device_model_object_called = false; + bool add_device_model_object_called = false; + + // callback stubs + nmos::filter_property_value_holders_handler filter_property_value_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + { + filter_property_value_holders_called = true; + auto modifiable_property_value_holders = value::array(); + + for (const auto& property_value : property_values) + { + web::json::push_back(modifiable_property_value_holders, property_value); + } + return modifiable_property_value_holders.as_array(); + }; + + nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::nc_oid reference_oid, bool validate) + { + remove_device_model_object_called = true; + + return true; + }; + + nmos::add_device_model_object_handler add_device_model_object = [&](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate) + { + add_device_model_object_called = true; + const auto& role_path = nmos::fields::nc::path(object_properties_holder); + return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::ok, U("OK")); + }; + + { + // Check new oid is generated for new device model object + // + filter_property_value_holders_called = false; + remove_device_model_object_called = false; + add_device_model_object_called = false; + + auto monitor_3_oid = 999; + // Create Object Properties Holder for Block, with a Property Holder for the block members + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers") }); + { + auto property_value_holders = value::array(); + auto members = value::array(); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, false, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + // Create Object Properties Holder for new monitor + const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + { + auto property_value_holders = value::array(); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); + { + auto property_value_holders = value::array(); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + bool validate = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + + // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors + BST_REQUIRE_EQUAL(3, output.as_array().size()); + + // Check the correct object properties holders have been returned + const auto& block_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), role_path.as_array()); + BST_REQUIRE_EQUAL(block_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = block_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + // Expect a warning that the oid for mon3 has changed + BST_REQUIRE_EQUAL(1, nmos::fields::nc::notices(object_properties_set_validation).size()); + const auto& notice = nmos::fields::nc::notices(object_properties_set_validation).at(0); + BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::warning, nmos::fields::nc::notice_type(notice)); + const auto& property_id = nmos::fields::nc::id(notice); + BST_CHECK_EQUAL(nmos::fields::nc::level(property_id), nmos::nc_block_members_property_id.level); + BST_CHECK_EQUAL(nmos::fields::nc::index(property_id), nmos::nc_block_members_property_id.index); + } + const auto& monitor_2_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_2_role_path.as_array()); + BST_REQUIRE_EQUAL(monitor_2_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = monitor_2_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + } + const auto& monitor_3_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_3_role_path.as_array()); + BST_REQUIRE_EQUAL(monitor_3_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = monitor_3_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(1, nmos::fields::nc::notices(object_properties_set_validation).size()); + const auto& notice = nmos::fields::nc::notices(object_properties_set_validation).at(0); + BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::warning, nmos::fields::nc::notice_type(notice)); + const auto& property_id = nmos::fields::nc::id(notice); + BST_CHECK_EQUAL(nmos::fields::nc::level(property_id), nmos::nc_object_oid_property_id.level); + BST_CHECK_EQUAL(nmos::fields::nc::index(property_id), nmos::nc_object_oid_property_id.index); + } + + BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!remove_device_model_object_called); + BST_CHECK(add_device_model_object_called); + } + { + // Handle constant oid clash + // + filter_property_value_holders_called = false; + remove_device_model_object_called = false; + add_device_model_object_called = false; + + // Create Object Properties Holder for Block, with a Property Holder for the block members + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers") }); + { + auto property_value_holders = value::array(); + auto members = value::array(); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + { + auto property_value_holders = value::array(); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + // Create Object Properties Holder for new monitor + const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); + { + auto property_value_holders = value::array(); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + bool validate = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + + // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors + BST_REQUIRE_EQUAL(3, output.as_array().size()); + + // Check the correct object properties holders have been returned + const auto& block_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), role_path.as_array()); + BST_REQUIRE_EQUAL(block_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = block_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); + } + const auto& monitor_2_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_2_role_path.as_array()); + BST_REQUIRE_EQUAL(monitor_2_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = monitor_2_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + } + const auto& monitor_3_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_3_role_path.as_array()); + BST_REQUIRE_EQUAL(monitor_3_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = monitor_3_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::device_error, nmos::fields::nc::status(object_properties_set_validation)); + } + + BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!remove_device_model_object_called); + BST_CHECK(!add_device_model_object_called); } } @@ -1208,7 +1436,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const auto& object_properties_set_validation = output.as_array().at(0); BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); - BST_CHECK_EQUAL(nmos::nc_restore_validation_status::device_error, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); BST_REQUIRE_EQUAL(1, nmos::fields::nc::notices(object_properties_set_validation).size()); const auto& notice = nmos::fields::nc::notices(object_properties_set_validation).at(0); BST_CHECK_EQUAL(nmos::fields::nc::notice_type(notice), nmos::nc_property_restore_notice_type::error); @@ -1221,7 +1449,62 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) BST_CHECK(!add_device_model_object_called); } { - // Check add_device_model_object_called error is handlerd when trying to modify a rebuildable block + // Check on remove_device_model_object_called error all other object properties holders are processed + // + filter_property_value_holders_called = false; + remove_device_model_object_called = false; + add_device_model_object_called = false; + + // Create Object Properties Holder + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers") }); + auto property_value_holders = value::array(); + auto members = value::array(); + + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + { + auto property_value_holders = value::array(); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + bool validate = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + + // expectation is there will be a result for each of the object_properties_holders + BST_CHECK_EQUAL(2, output.as_array().size()); + + // Check the correct object properties holders have been returned + const auto& block_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), role_path.as_array()); + BST_REQUIRE_EQUAL(block_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = block_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(1, nmos::fields::nc::notices(object_properties_set_validation).size()); + const auto& notice = nmos::fields::nc::notices(object_properties_set_validation).at(0); + BST_CHECK_EQUAL(nmos::fields::nc::notice_type(notice), nmos::nc_property_restore_notice_type::error); + const auto& property_id = nmos::fields::nc::id(notice); + BST_CHECK_EQUAL(nmos::fields::nc::level(property_id), nmos::nc_block_members_property_id.level); + BST_CHECK_EQUAL(nmos::fields::nc::index(property_id), nmos::nc_block_members_property_id.index); + } + const auto& monitor_1_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_1_role_path.as_array()); + BST_REQUIRE_EQUAL(monitor_1_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = monitor_1_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + } + } + { + // Check add_device_model_object_called error is handled when trying to modify a rebuildable block // filter_property_value_holders_called = false; remove_device_model_object_called = false; @@ -1268,7 +1551,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors - BST_REQUIRE_EQUAL(4, output.as_array().size()); + BST_CHECK_EQUAL(4, output.as_array().size()); // Check the correct object properties holders have been returned const auto& block_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), role_path.as_array()); @@ -1304,4 +1587,60 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) BST_CHECK(!remove_device_model_object_called); BST_CHECK(add_device_model_object_called); } + { + // Check duplicate block object properties holders are handled + // + filter_property_value_holders_called = false; + remove_device_model_object_called = false; + add_device_model_object_called = false; + + // Create Object Properties Holder + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers") }); + auto property_value_holders1 = value::array(); + auto members1 = value::array(); + auto property_value_holders2 = value::array(); + auto members2 = value::array(); + + push_back(members1, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_value_holders1, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members1)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders1.as_array(), value::array().as_array(), value::array().as_array(), false)); + // duplicate + push_back(members2, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_value_holders2, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members2)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders2.as_array(), value::array().as_array(), value::array().as_array(), false)); + const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); + { + auto property_value_holders = value::array(); + push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + bool validate = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + + const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + + // expectation is there will be a result for each of the object_properties_holders + BST_CHECK_EQUAL(3, output.as_array().size()); + + // Check the correct object properties holders have been returned + const auto& block_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), role_path.as_array()); + BST_REQUIRE_EQUAL(block_object_properties_holder.size(), 2); // expect a duplicate + { + const auto& object_properties_set_validation0 = block_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation0)); + const auto& object_properties_set_validation1 = block_object_properties_holder.at(1); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation1)); + } + const auto& monitor_1_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_1_role_path.as_array()); + BST_REQUIRE_EQUAL(monitor_1_object_properties_holder.size(), 1); + { + const auto& object_properties_set_validation = monitor_1_object_properties_holder.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); + } + } } From c642f6269dd62276fee704c9dacc34c0a05f6dcf Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 23 Jun 2025 15:01:39 +0100 Subject: [PATCH 190/250] Refactor configuration utils (cherry picked from commit f912ade31b0570ea28c2b789170d5ac4a6d94005) --- Development/nmos/configuration_utils.cpp | 323 ++++++++---------- Development/nmos/configuration_utils.h | 3 - .../nmos/test/configuration_utils_test.cpp | 77 +---- 3 files changed, 142 insertions(+), 261 deletions(-) diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 0e34855da..7ae84e33d 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -13,6 +13,8 @@ namespace nmos { + typedef std::map object_properties_map; + namespace details { bool is_property_value_valid(web::json::value& property_restore_notices, const web::json::value& property_value, const web::json::value& property_descriptor, const web::json::value& restore_mode, bool is_rebuildable) @@ -72,7 +74,7 @@ namespace nmos return false; } - web::json::value modify_device_model_object(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& target_object_properties_holder, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders) + web::json::value modify_device_model_object(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& target_object_properties_holder, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders) { const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); @@ -101,7 +103,7 @@ namespace nmos // If the property_modify_list contains read only properties then we call back to the application code to // check that it's OK to change those value. Bear in mind that these could be the class Id, or the oid or some other // property that we don't want changed ordinarily - property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list, recurse, validate, property_restore_notices.as_array(), get_control_protocol_class_descriptor); + property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list, true, validate, property_restore_notices.as_array(), get_control_protocol_class_descriptor); } else { @@ -130,16 +132,14 @@ namespace nmos return nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, property_restore_notices.as_array(), U("OK")); } - web::json::value modify_rebuildable_block(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, nmos::filter_property_value_holders_handler filter_property_value_holders) + web::json::value modify_rebuildable_block(nmos::resources& resources, object_properties_map& object_properties_holder_map, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& block_object_properties_holder, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, nmos::filter_property_value_holders_handler filter_property_value_holders) { - // rebuildable block and child objects are passed to this function for modification auto object_properties_set_validations = web::json::value::array(); // Find object_properties_holder for resource - // the parent function guarantees that this target role path is in the object_properties_holders + // the calling function guarantees that this target role path is in the object_properties_holders // and property_holder for block members exists - const auto& filtered_holders = nmos::get_object_properties_holder(object_properties_holders, target_role_path); - const auto& block_members_properties_holder = nmos::get_property_value_holder(*filtered_holders.begin(), nmos::nc_block_members_property_id); + const auto& block_members_properties_holder = nmos::get_property_value_holder(block_object_properties_holder, nmos::nc_block_members_property_id); const auto& restore_members = nmos::fields::nc::value(block_members_properties_holder); const auto& reference_members = nmos::fields::nc::members(resource.data); @@ -181,8 +181,6 @@ namespace nmos auto notices = web::json::value::array(); const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Unable to delete resource from Device Model.")); web::json::push_back(block_notices, notice); - - continue; } } } @@ -202,14 +200,7 @@ namespace nmos { // can't find this role in existing members, so member need to be added // Find the object_properties_holder that describes the receiver monitor object - const auto& filtered_child_object_properties_holders = boost::copy_range>(object_properties_holders - | boost::adaptors::filtered([&child_role_path](const web::json::value& object_properties_holder) - { - return nmos::fields::nc::path(object_properties_holder) == child_role_path.as_array(); - }) - ); - - if (filtered_child_object_properties_holders.size() != 1) + if (object_properties_holder_map.find(child_role_path.as_array()) == object_properties_holder_map.end()) { auto status_message = U("Cannot find NcObjectPropertiesHolder for new resource"); auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); @@ -218,7 +209,7 @@ namespace nmos continue; } - const auto& child_object_properties_holder = *filtered_child_object_properties_holders.begin(); + const auto& child_object_properties_holder = object_properties_holder_map.find(child_role_path.as_array()); // Get member descriptor properties auto role = nmos::fields::nc::role(restore_member); @@ -230,7 +221,7 @@ namespace nmos auto added_object_notices = web::json::value::array(); - const auto& oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_oid_property_id); + const auto& oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder->second, nmos::nc_object_oid_property_id); oid = oid_property_holder == web::json::value::null() ? oid : nmos::fields::nc::value(oid_property_holder).as_integer(); if (oid_property_holder != web::json::value::null() && oid != nmos::fields::nc::value(oid_property_holder).as_integer()) @@ -242,6 +233,7 @@ namespace nmos if (constant_oid) { // If constant oid then check oid from property holder and verify with oid from block member + // Is this oid already in use? const auto& child = find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != child) @@ -254,6 +246,8 @@ namespace nmos // also create error notice for the block const auto block_notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, status_message); web::json::push_back(block_notices, block_notice); + // erase object from object_properties_holder_map so it isn't processed subsequently + object_properties_holder_map.erase(child_role_path.as_array()); continue; } } @@ -273,9 +267,9 @@ namespace nmos web::json::push_back(added_object_notices, added_object_notice); } - // The values in the block member Object Property Holder will take precidence over the block member descriptor values - // If the block member values are inconsistant then warn - description and user label are independant - const auto& role_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_role_property_id); + // The values in the block member object properties holder will take precidence over the block member descriptor values + // If the block member values are inconsistant then warn - description and user label values can be independant + const auto& role_property_holder = nmos::get_property_value_holder(child_object_properties_holder->second, nmos::nc_object_role_property_id); role = role_property_holder == web::json::value::null() ? role : nmos::fields::nc::value(role_property_holder).as_string(); if (role_property_holder != web::json::value::null() && role != nmos::fields::nc::value(role_property_holder).as_string()) @@ -292,7 +286,7 @@ namespace nmos const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); web::json::push_back(block_notices, notice); } - const auto& owner_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_owner_property_id); + const auto& owner_property_holder = nmos::get_property_value_holder(child_object_properties_holder->second, nmos::nc_object_owner_property_id); if (owner_property_holder != web::json::value::null() && block_oid != nmos::fields::nc::value(owner_property_holder).as_integer()) { utility::stringstream_t ss; @@ -300,7 +294,7 @@ namespace nmos const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); web::json::push_back(added_object_notices, notice); } - const auto& constant_oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_constant_oid_property_id); + const auto& constant_oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder->second, nmos::nc_object_constant_oid_property_id); constant_oid = constant_oid_property_holder == web::json::value::null() ? constant_oid : nmos::fields::nc::value(constant_oid_property_holder).as_bool(); if (constant_oid_property_holder != web::json::value::null() && constant_oid != nmos::fields::nc::value(constant_oid_property_holder).as_bool()) @@ -311,16 +305,17 @@ namespace nmos web::json::push_back(block_notices, notice); } - const auto& user_label_property_holder = nmos::get_property_value_holder(child_object_properties_holder, nmos::nc_object_user_label_property_id); + const auto& user_label_property_holder = nmos::get_property_value_holder(child_object_properties_holder->second, nmos::nc_object_user_label_property_id); const auto& user_label = (user_label_property_holder == web::json::value::null()) ? block_member_user_label : nmos::fields::nc::value(user_label_property_holder).as_string(); - auto object_properties_set_validation = add_device_model_object(child_object_properties_holder, oid, owner, role, user_label, validate); + auto object_properties_set_validation = add_device_model_object(child_object_properties_holder->second, oid, owner, role, user_label, validate); // Add warnings about known inconsistancies between backup dataset and new device model object for (const auto& added_object_notice: added_object_notices.as_array()) { web::json::push_back(nmos::fields::nc::notices(object_properties_set_validation), added_object_notice); } + // If the status is anything other than OK assume the object wasn't created if (nmos::fields::nc::status(object_properties_set_validation) == nmos::nc_restore_validation_status::ok && !validate) { auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(block_member_description, role, oid, constant_oid, nmos::nc_receiver_monitor_class_id, block_member_user_label, owner); @@ -328,17 +323,9 @@ namespace nmos } web::json::push_back(object_properties_set_validations, object_properties_set_validation); - } - else - { - // If this member had a corresponding child object properties holder then update - const auto& child_object_properties_holder = nmos::get_object_properties_holder(object_properties_holders, child_role_path.as_array()); - if (child_object_properties_holder.size()) - { - const auto& child = find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(restore_member)))); - const auto object_properties_set_validation = details::modify_device_model_object(resources, *child, child_role_path.as_array(), child_object_properties_holder.at(0), recurse, nmos::nc_restore_mode::rebuild, validate, get_control_protocol_class_descriptor, filter_property_value_holders); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); - } + + // erase object from object_properties_holder_map so it isn't processed subsequently + object_properties_holder_map.erase(child_role_path.as_array()); } } // If there are any error notices, then give an overall error status for object properties set validation @@ -390,6 +377,54 @@ namespace nmos } } + web::json::array 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 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()) + { + 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); + web::json::push_back(role_path, nmos::fields::nc::role(found_resource.data)); + oid = nmos::fields::nc::id(found_resource.data); + } + + std::reverse(role_path.as_array().begin(), role_path.as_array().end()); + + return role_path.as_array(); + } + + web::json::value get_property_value_holder(const web::json::value& object_properties_holder, const nmos::nc_property_id& property_id) + { + const auto& filtered_property_value_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) + | boost::adaptors::filtered([&property_id](const web::json::value& property_value_holder) + { + return property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value_holder)); + }) + ); + // There should only be a single property holder for the members + if (filtered_property_value_holders.size() != 1) + { + // Error + return web::json::value::null(); + } + + return *filtered_property_value_holders.begin(); + } + // Check to see if root_role_path is root of role_path bool is_role_path_root(const web::json::array& role_path_root, const web::json::array& role_path) { @@ -472,188 +507,108 @@ namespace nmos return web::json::value_from_elements(target_object_properties_holders).as_array(); } - web::json::array get_child_object_properties_holders(const web::json::array& object_properties_holders, const web::json::array& target_role_path) - { - const auto& child_object_properties_holders = boost::copy_range>(object_properties_holders - | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) - { - return is_role_path_root(target_role_path, nmos::fields::nc::path(object_properties_holder)); - }) - ); - return web::json::value_from_elements(child_object_properties_holders).as_array(); - } - - web::json::value modify_device_model(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) { auto object_properties_set_validation_values = web::json::value::array(); - // Filter for the target_role_path and child objects - // - const auto& child_object_properties_holders = get_child_object_properties_holders(object_properties_holders, target_role_path); - - // get object_properties_holder for the target role path, if there is one - const auto& target_object_properties_holders = get_object_properties_holder(object_properties_holders, target_role_path); + const auto target_role_path = get_role_path(resources, resource); - // there should be 0 or 1 object_properties_holder for any role path. - if (target_object_properties_holders.size() > 1) - { - // Error in the backup dataset - // Generate errors for all object properties holders - utility::stringstream_t ss; - ss << U("duplicate object_properties_holder for role path: "); - for (const auto& element: target_role_path) - { - ss << element << " "; - } - for (const auto& object_properties_holder: object_properties_holders) - { - ss << "."; // hmmmm, object properties set validations with identical data can't be filtered, so vary error message slightly - const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(nmos::fields::nc::path(object_properties_holder), nmos::nc_restore_validation_status::failed, ss.str()); - web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); - } - return object_properties_set_validation_values; - } + // Process blocks and objects separately. + object_properties_map object_properties_holder_map; + std::vector< web::json::array > role_paths; - const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + utility::stringstream_t seed; - if (recurse && nmos::nc::is_block(class_id)) + for (const auto& object_properties_holder: object_properties_holders) { - if (target_object_properties_holders.size()) + const auto& role_path = nmos::fields::nc::path(object_properties_holder); + // Only process role paths within the restore scope, or target_role_path only + if ((recurse && is_role_path_root(target_role_path, nmos::fields::nc::path(object_properties_holder))) || (!recurse && target_role_path == role_path)) { - if (nmos::fields::nc::is_rebuildable(resource.data) && restore_mode == nmos::nc_restore_mode::rebuild && is_block_modified(resource, *target_object_properties_holders.begin())) + const auto& find_role_paths = get_object_properties_holder(object_properties_holders, role_path); + + // Make errors for duplicate role paths + if (find_role_paths.size() > 1) { - // Modify rebuildable block - if (remove_device_model_object && add_device_model_object) + seed << "."; // to avoid making identical objects + utility::stringstream_t ss; + ss << U("Duplicate object_properties_holder for role path: "); + for (const auto& element: role_path) { - // Process this block and all children of this block - return details::modify_rebuildable_block(resources, resource, target_role_path, child_object_properties_holders, recurse, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object, filter_property_value_holders); - } - else - { - // Rebuilding blocks not supported - const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, U("Rebuilding of Device Model blocks not supported")); - web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + ss << element << "."; } + ss << seed.str(); + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(nmos::fields::nc::path(object_properties_holder), nmos::nc_restore_validation_status::failed, ss.str()); + web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); } else { - // Modify non-rebuiladable block - const auto object_properties_set_validation = details::modify_device_model_object(resources, resource, target_role_path, target_object_properties_holders.at(0), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders); - web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + object_properties_holder_map.insert({ role_path, object_properties_holder }); + role_paths.push_back(role_path); } } - // iterate through child objects - if (resource.data.has_field(nmos::fields::nc::members)) - { - const auto& members = nmos::fields::nc::members(resource.data); + } - for (const auto& member : members) - { - const auto& child = find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(member)))); + // Order role paths by length so we implicitly process blocks before the child objects of that block + std::sort(role_paths.begin(), role_paths.end(), [](auto a, auto b) { return a.size() < b.size(); }); - if (resources.end() != child) - { - // Append the role of the child to the target role path to create the child role path - auto child_role_path = web::json::value_from_elements(target_role_path); - web::json::push_back(child_role_path, nmos::fields::nc::role(child->data)); + for (const auto& role_path: role_paths) + { + const auto& r = nc::find_resource_by_role_path(resources, role_path); - web::json::value child_object_properties_set_validation_values = modify_device_model(resources, *child, child_role_path.as_array(), child_object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); - // Hmmm, there must be a better way of merging two json array objects - for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) - { - web::json::push_back(object_properties_set_validation_values, validation_values); - } - } - } + if (r == resources.end()) + { + // This could be a resource that's yet to be created in Rebuild mode (OK), or referencing a resource that doesn't exist (not OK) + // Ignore for now and we will check again once all role paths have been processed + continue; } - } - else - { - if (target_object_properties_holders.size()) + if (object_properties_holder_map.find(role_path) == object_properties_holder_map.end()) { - // Modify object - const auto object_properties_set_validation = details::modify_device_model_object(resources, resource, target_role_path, target_object_properties_holders.at(0), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders); - web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + // If this role_path is no longer in the map it may have been erased by modify_rebuildable_block + continue; } - } - return object_properties_set_validation_values; - } - web::json::array 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; + const auto& object_properties_holder = object_properties_holder_map.at(role_path); - auto role_path = value::array(); - web::json::push_back(role_path, nmos::fields::nc::role(resource.data)); + const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(r->data)); - auto oid = nmos::fields::nc::id(resource.data); - nmos::resource found_resource = resource; + if (nmos::nc::is_block(class_id) && nmos::fields::nc::is_rebuildable(r->data) && restore_mode == nmos::nc_restore_mode::rebuild && is_block_modified(*r, object_properties_holder)) + { + // Modify rebuildable block + if (remove_device_model_object && add_device_model_object) + { + // Process this block to add / remove device model objects as members of this block + // the object properties holder for any added objects will be erased from the object_properties_holder_map to avoid double processing + const auto& child_object_properties_set_validations = details::modify_rebuildable_block(resources, object_properties_holder_map, *r, role_path, object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object, filter_property_value_holders); + for (const auto& validation_values : child_object_properties_set_validations.as_array()) + { + web::json::push_back(object_properties_set_validation_values, validation_values); + } - while (utility::s2us(std::to_string(nmos::root_block_oid)) != oid.as_string()) - { - const auto& found = nmos::find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::owner(found_resource.data)))); - if (resources.end() == found) + } + else + { + // Rebuilding blocks not supported + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::failed, U("Rebuilding of Device Model blocks not supported")); + web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); + } + } + else { - break; + const auto object_properties_set_validation = details::modify_device_model_object(resources, *r, role_path, object_properties_holder, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders); + web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); } - found_resource = (*found); - web::json::push_back(role_path, nmos::fields::nc::role(found_resource.data)); - oid = nmos::fields::nc::id(found_resource.data); + object_properties_holder_map.erase(role_path); } - std::reverse(role_path.as_array().begin(), role_path.as_array().end()); - - return role_path.as_array(); - } - - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) - { - auto object_properties_set_validation_values = web::json::value::array(); - - const auto target_role_path = get_role_path(resources, resource); - - // Detect and warn if there are any object_properties_holders outside of the target role path's scope - // Hmmm, can this be done as a one step process rather than filtering and then iterating over filtered list? - const auto& orphan_object_properties_holders = boost::copy_range>(object_properties_holders - | boost::adaptors::filtered([&target_role_path](const web::json::value& object_properties_holder) - { - return !nmos::is_role_path_root(target_role_path, nmos::fields::nc::path(object_properties_holder)); - }) - ); - for (const auto& orphan_object_properties_holder : orphan_object_properties_holders) + // What ever remains is referencing an object not in the device model + for(const auto& object_properties_holder: object_properties_holder_map) { - const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(nmos::fields::nc::path(orphan_object_properties_holder), nmos::nc_restore_validation_status::not_found, U("object role path not found under target role path")); + const auto& object_properties_set_validation = nmos::make_object_properties_set_validation(object_properties_holder.first, nmos::nc_restore_validation_status::not_found, U("Unknown role path")); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); } - web::json::value child_object_properties_set_validation_values = modify_device_model(resources, resource, target_role_path, object_properties_holders, recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); - // Hmmm - there must be a better way to append an array - for (const auto& validation_values : child_object_properties_set_validation_values.as_array()) - { - web::json::push_back(object_properties_set_validation_values, validation_values); - } - return object_properties_set_validation_values; } - - web::json::value get_property_value_holder(const web::json::value& object_properties_holder, const nmos::nc_property_id& property_id) - { - const auto& filtered_property_value_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) - | boost::adaptors::filtered([&property_id](const web::json::value& property_value_holder) - { - return property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value_holder)); - }) - ); - // There should only be a single property holder for the members - if (filtered_property_value_holders.size() != 1) - { - // Error - return web::json::value::null(); - } - - return *filtered_property_value_holders.begin(); - } } diff --git a/Development/nmos/configuration_utils.h b/Development/nmos/configuration_utils.h index 1ead1f953..13847d67c 100644 --- a/Development/nmos/configuration_utils.h +++ b/Development/nmos/configuration_utils.h @@ -20,9 +20,6 @@ namespace nmos // Get object_properties_holder for specified target_role_path web::json::array get_object_properties_holder(const web::json::array& object_properties_holders, const web::json::array& target_role_path); - // Get object_properties_holder for specified target_role_path and all its child object_properties_holders - web::json::array get_child_object_properties_holders(const web::json::array& object_properties_holders, const web::json::array& target_role_path); - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); web::json::value get_property_value_holder(const web::json::value& object_properties_holder, const nmos::nc_property_id& property_id); diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 9c616dc5e..813938f52 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -282,72 +282,6 @@ BST_TEST_CASE(testGetObjectPropertiesHolder) } } -//////////////////////////////////////////////////////////////////////////////////////////// -BST_TEST_CASE(testGetChildObjectPropertiesHolders) -{ - using web::json::value_of; - using web::json::value; - - // Create Object Properties Holder - auto object_properties_holders = value::array(); - - { - const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - auto property_value_holders = value::array(); - const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); - push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); - push_back(object_properties_holders, object_properties_holder); - } - { - const auto role_path = value_of({ U("root"), U("receivers"), U("mon2") }); - auto property_value_holders = value::array(); - const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); - push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); - push_back(object_properties_holders, object_properties_holder); - } - { - const auto role_path = value_of({ U("root"), U("senders"), U("mon1") }); - auto property_value_holders = value::array(); - const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); - push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); - push_back(object_properties_holders, object_properties_holder); - } - - { - const auto target_role_path = value_of({ U("root"), U("receivers") }); - - const auto child_object_properties_holders = nmos::get_child_object_properties_holders(object_properties_holders.as_array(), target_role_path.as_array()); - - BST_REQUIRE_EQUAL(2, child_object_properties_holders.size()); - - const auto& object_properties_holder1 = nmos::get_object_properties_holder(child_object_properties_holders, value_of({ U("root"), U("receivers"), U("mon1") }).as_array()); - BST_CHECK_EQUAL(1, object_properties_holder1.size()); - - const auto& object_properties_holder2 = nmos::get_object_properties_holder(child_object_properties_holders, value_of({ U("root"), U("receivers"), U("mon2") }).as_array()); - BST_CHECK_EQUAL(1, object_properties_holder2.size()); - } - { - const auto target_role_path = value_of({ U("root"), U("receivers"), U("mon1")}); - - const auto child_object_properties_holders = nmos::get_child_object_properties_holders(object_properties_holders.as_array(), target_role_path.as_array()); - - BST_REQUIRE_EQUAL(1, child_object_properties_holders.size()); - - const auto& object_properties_holder1 = nmos::get_object_properties_holder(child_object_properties_holders, value_of({ U("root"), U("receivers"), U("mon1") }).as_array()); - BST_CHECK_EQUAL(1, object_properties_holder1.size()); - } - { - const auto target_role_path = value_of({ U("root"), U("does_not_exist") }); - - const auto child_object_properties_holders = nmos::get_child_object_properties_holders(object_properties_holders.as_array(), target_role_path.as_array()); - - BST_REQUIRE_EQUAL(0, child_object_properties_holders.size()); - } -} - //////////////////////////////////////////////////////////////////////////////////////////// BST_TEST_CASE(testGetRolePath) { @@ -778,13 +712,8 @@ BST_TEST_CASE(testApplyBackupDataSet) const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); - // expectation is there will be a result for each of the object_properties_holders i.e. one - BST_REQUIRE_EQUAL(1, output.as_array().size()); - const auto object_properties_set_validation = output.as_array().at(0); - - BST_CHECK_EQUAL(role_path.as_array(), nmos::fields::nc::path(object_properties_set_validation)); - BST_CHECK_EQUAL(nmos::nc_restore_validation_status::not_found, nmos::fields::nc::status(object_properties_set_validation)); - BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); + // expectation no object_properties_holders as not in the restore scope + BST_REQUIRE_EQUAL(0, output.as_array().size()); BST_CHECK(!filter_property_value_holders_called); BST_CHECK(!remove_device_model_object_called); @@ -1640,7 +1569,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) BST_REQUIRE_EQUAL(monitor_1_object_properties_holder.size(), 1); { const auto& object_properties_set_validation = monitor_1_object_properties_holder.at(0); - BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); } } } From 2bf92713b701367fcf3c04225901e6f92b7f0b7d Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Mon, 23 Jun 2025 15:45:25 +0100 Subject: [PATCH 191/250] Improve user example code --- .../nmos-cpp-node/node_implementation.cpp | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 1949732ca..a5032c82a 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1794,7 +1794,6 @@ nmos::remove_device_model_object_handler make_remove_device_model_handler(nmos:: { return true; } - const auto oid = nmos::fields::nc::oid(found->data); auto erase_count = nmos::nc::erase_resource(control_protocol_resources, found->id); if (erase_count > 0) { @@ -1812,8 +1811,7 @@ nmos::add_device_model_object_handler make_add_device_model_object_handler(nmos: // This example callback shows how to add a receiver monitor resource to the device model // The receivers block that contains the monitors must be rebuildable // To add a monitor, restore a backup dataset including an additional monitor in the members property of the receivers block, in Rebuild mode - // Also include an object properties holder for the new monitor including a touchpoint refencing the NMOS Receiver resource being monitored - // JRT TODO: Add some boiler plate to add notices for unused property value holders + // Also include an object properties holder for the new monitor including a touchpoint property holder refencing the NMOS Receiver resource being monitored nmos::resources& control_protocol_resources = model.control_protocol_resources; const auto& role_path = nmos::fields::nc::path(object_properties_holder); @@ -1856,7 +1854,25 @@ nmos::add_device_model_object_handler make_add_device_model_object_handler(nmos: auto receiver_monitor = nmos::make_receiver_monitor(oid, true, owner, role, user_label, U(""), web::json::value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})} })); nmos::nc::insert_resource(control_protocol_resources, std::move(receiver_monitor)); } - return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::ok); + // Generate notices for any properties that have been unprocessed + std::vector< nmos::nc_property_id > processed_properties = { nmos::nc_object_oid_property_id, + nmos::nc_object_owner_property_id, + nmos::nc_object_role_property_id, + nmos::nc_object_user_label_property_id, + nmos::nc_object_touchpoints_property_id }; + auto notices = web::json::value::array(); + for (const auto& property_holder: nmos::fields::nc::values(object_properties_holder)) + { + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)); + const auto& name = nmos::fields::nc::name(property_holder).c_str(); + if (std::find(processed_properties.begin(), processed_properties.end(), property_id) == processed_properties.end()) + { + const auto notice = nmos::details::make_nc_property_restore_notice(property_id, name, nmos::nc_property_restore_notice_type::warning, U("Property unprocessed.")); + web::json::push_back(notices, notice); + } + } + + return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::ok, notices.as_array()); }; } From ae41ee9c84c4ed1209a2b569ff364e54a2264f06 Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Tue, 24 Jun 2025 15:58:37 +0100 Subject: [PATCH 192/250] Renames NcPropertyValueHolder to NcPropertyHolder and BulkValues types to BulkProperties --- Development/nmos-cpp-node/main.cpp | 2 +- .../nmos-cpp-node/node_implementation.cpp | 16 +- Development/nmos/configuration_api.cpp | 20 +- Development/nmos/configuration_api.h | 2 +- Development/nmos/configuration_handlers.h | 2 +- Development/nmos/configuration_methods.cpp | 30 +- Development/nmos/configuration_methods.h | 4 +- Development/nmos/configuration_utils.cpp | 42 +- Development/nmos/configuration_utils.h | 4 +- .../nmos/control_protocol_resource.cpp | 30 +- Development/nmos/control_protocol_resource.h | 12 +- Development/nmos/control_protocol_state.cpp | 28 +- Development/nmos/control_protocol_state.h | 2 +- Development/nmos/json_fields.h | 2 +- Development/nmos/node_server.cpp | 2 +- Development/nmos/node_server.h | 8 +- .../nmos/test/configuration_methods_test.cpp | 12 +- .../nmos/test/configuration_utils_test.cpp | 450 +++++++++--------- 18 files changed, 334 insertions(+), 334 deletions(-) diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index 443e14c21..07c1d9011 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -138,7 +138,7 @@ int main(int argc, char* argv[]) .on_request_authorization_code(nmos::experimental::make_request_authorization_code_handler(gate)); // may be omitted, only required for OAuth client which is using the Authorization Code Flow to obtain the access token } - nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.filter_property_value_holders, node_implementation.remove_device_model_object, node_implementation.add_device_model_object); + nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.filter_property_holders, node_implementation.remove_device_model_object, node_implementation.add_device_model_object); if (0 <= nmos::fields::control_protocol_ws_port(node_model.settings)) { node_implementation diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index a5032c82a..263725fc7 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1743,16 +1743,16 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control // Example Device Configuration callback called when a rebuildable object is modified in Rebuild mode. // An array of property values is passed in, and an array of property values that can be modified is returned // For each property value that can't be returned a property restore notice must be created -nmos::filter_property_value_holders_handler make_filter_property_value_holders_handler(nmos::resources& resources, slog::base_gate& gate) +nmos::filter_property_holders_handler make_filter_property_holders_handler(nmos::resources& resources, slog::base_gate& gate) { return [&resources, &gate](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { // Use this function to filter which of the properties in the object should be modified by the configuration API - slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do filter_property_value_holders"; + slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do filter_property_holders"; nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); - auto modifiable_property_value_holders = web::json::value::array(); + auto modifiable_property_holders = web::json::value::array(); for (const auto& property_value : property_values) { @@ -1772,10 +1772,10 @@ nmos::filter_property_value_holders_handler make_filter_property_value_holders_h } else { - web::json::push_back(modifiable_property_value_holders, property_value); + web::json::push_back(modifiable_property_holders, property_value); } } - return modifiable_property_value_holders.as_array(); + return modifiable_property_holders.as_array(); }; } @@ -1834,10 +1834,10 @@ nmos::add_device_model_object_handler make_add_device_model_object_handler(nmos: return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::device_error, status_message); } } - const auto& touchpoint_property_holder = nmos::get_property_value_holder(object_properties_holder, nmos::nc_object_touchpoints_property_id); + const auto& touchpoint_property_holder = nmos::get_property_holder(object_properties_holder, nmos::nc_object_touchpoints_property_id); if (touchpoint_property_holder == web::json::value::null()) { - auto status_message = U("Cannot find touchpoint object property value holder"); + auto status_message = U("Cannot find touchpoint object property holder"); return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::failed, status_message); } @@ -2031,7 +2031,7 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode .on_validate_channelmapping_output_map(make_node_implementation_map_validator()) // may be omitted if not required .on_channelmapping_activated(make_node_implementation_channelmapping_activation_handler(gate)) .on_control_protocol_property_changed(make_node_implementation_control_protocol_property_changed_handler(gate)) // may be omitted if IS-12 not required - .on_filter_property_value_holders(make_filter_property_value_holders_handler(model.control_protocol_resources, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required + .on_filter_property_holders(make_filter_property_holders_handler(model.control_protocol_resources, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required .on_remove_device_model_object(make_remove_device_model_handler(model, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required .on_add_device_model_object(make_add_device_model_object_handler(model, gate)); // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required } diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 881b26ca6..03bcd5c59 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -18,9 +18,9 @@ namespace nmos { - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -51,7 +51,7 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object, property_changed, gate)); + configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object, property_changed, gate)); return configuration_api; } @@ -148,7 +148,7 @@ namespace nmos } } - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -660,12 +660,12 @@ namespace nmos }); // PATCH /rolePaths/{rolePath}/bulkProperties - invokes validation_set_properties_by_path method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object, version, &gate_](value body) mutable + return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object, version, &gate_](value body) mutable { auto lock = model.write_lock(); auto& resources = model.control_protocol_resources; @@ -685,7 +685,7 @@ namespace nmos const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); const auto& backup_data_set = nmos::fields::nc::data_set(arguments); - method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); auto status = nmos::fields::nc::status(method_result); if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } @@ -724,12 +724,12 @@ namespace nmos }); // PUT /rolePaths/{rolePath}/bulkProperties - invokes set_properties_by_path method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object, version, &gate_](value body) mutable + return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object, version, &gate_](value body) mutable { auto lock = model.write_lock(); auto& resources = model.control_protocol_resources; @@ -749,7 +749,7 @@ namespace nmos const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); const auto& backup_data_set = nmos::fields::nc::data_set(arguments); - method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); code = status_codes::OK; diff --git a/Development/nmos/configuration_api.h b/Development/nmos/configuration_api.h index dc967c2e9..0ce11749a 100644 --- a/Development/nmos/configuration_api.h +++ b/Development/nmos/configuration_api.h @@ -16,7 +16,7 @@ namespace nmos { struct node_model; - web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); } #endif diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h index 99690969e..7066ca496 100644 --- a/Development/nmos/configuration_handlers.h +++ b/Development/nmos/configuration_handlers.h @@ -19,7 +19,7 @@ namespace nmos } // This callback is invoked if attempting to modify read only properties when restoring a configuration. // This function should modify the Device Model object directly and return a corresponding NcObjectPropertiesSetValidation object - typedef std::function filter_property_value_holders_handler; + typedef std::function filter_property_holders_handler; // This callback is invoked if attempting to modify a rebuildable block when restoring a configuration. // This function should handle the modification of the Device Model and any corresponding NMOS resources diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index 31f18ff36..c8f759364 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -13,40 +13,40 @@ namespace nmos { namespace details { - web::json::array make_property_value_holders(const nmos::resource& resource, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + web::json::array make_property_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(); + value property_holders = value::array(); nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); - // make NcPropertyValueHolder objects + // make NcPropertyHolder objects while (!class_id.empty()) { const auto& control_class_descriptor = get_control_protocol_class_descriptor(class_id); for (const auto& property_descriptor : control_class_descriptor.property_descriptors.as_array()) { - value property_value_holder = nmos::details::make_nc_property_value_holder(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)), nmos::fields::nc::name(property_descriptor), nmos::fields::nc::type_name(property_descriptor), nmos::fields::nc::is_read_only(property_descriptor), resource.data.at(nmos::fields::nc::name(property_descriptor))); + value property_holder = nmos::details::make_nc_property_holder(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)), nmos::fields::nc::name(property_descriptor), nmos::fields::nc::type_name(property_descriptor), nmos::fields::nc::is_read_only(property_descriptor), resource.data.at(nmos::fields::nc::name(property_descriptor))); - web::json::push_back(property_value_holders, property_value_holder); + web::json::push_back(property_holders, property_holder); } class_id.pop_back(); } - return property_value_holders.as_array(); + return property_holders.as_array(); } void populate_object_property_holder(const nmos::resources& resources, 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 auto& property_value_holders = make_property_value_holders(resource, get_control_protocol_class_descriptor); + // Get property_holders for this resource + const auto& property_holders = make_property_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, value::array().as_array(), value::array().as_array(), nmos::fields::nc::is_rebuildable(resource.data)); + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_holders, value::array().as_array(), value::array().as_array(), nmos::fields::nc::is_rebuildable(resource.data)); web::json::push_back(object_properties_holders, object_properties_holder); @@ -119,27 +119,27 @@ namespace nmos utility::ostringstream_t ss; ss << validation_fingerprint; - auto bulk_values_holder = nmos::details::make_nc_bulk_values_holder(ss.str(), object_properties_holders); + auto bulk_properties_holder = nmos::details::make_nc_bulk_properties_holder(ss.str(), object_properties_holders); - return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, bulk_values_holder); + return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, bulk_properties_holder); } - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) { // Do something with validation fingerprint? const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } - web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) { // Do something with validation fingerprint? const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } diff --git a/Development/nmos/configuration_methods.h b/Development/nmos/configuration_methods.h index cf6323c75..cd50cb19b 100644 --- a/Development/nmos/configuration_methods.h +++ b/Development/nmos/configuration_methods.h @@ -17,9 +17,9 @@ namespace nmos // Implementation of IS-14 function for creating backup dataset from a Device Model web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor); - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); - web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); } #endif diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 7ae84e33d..870df06e1 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -74,7 +74,7 @@ namespace nmos return false; } - web::json::value modify_device_model_object(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& target_object_properties_holder, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders) + web::json::value modify_device_model_object(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& target_object_properties_holder, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_holders_handler filter_property_holders) { const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); @@ -98,12 +98,12 @@ namespace nmos if (details::is_contains_read_only_property(property_modify_list, class_id, get_control_protocol_class_descriptor)) { - if (filter_property_value_holders) + if (filter_property_holders) { // If the property_modify_list contains read only properties then we call back to the application code to // check that it's OK to change those value. Bear in mind that these could be the class Id, or the oid or some other // property that we don't want changed ordinarily - property_modify_list = filter_property_value_holders(resource, target_role_path, property_modify_list, true, validate, property_restore_notices.as_array(), get_control_protocol_class_descriptor); + property_modify_list = filter_property_holders(resource, target_role_path, property_modify_list, true, validate, property_restore_notices.as_array(), get_control_protocol_class_descriptor); } else { @@ -132,14 +132,14 @@ namespace nmos return nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, property_restore_notices.as_array(), U("OK")); } - web::json::value modify_rebuildable_block(nmos::resources& resources, object_properties_map& object_properties_holder_map, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& block_object_properties_holder, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, nmos::filter_property_value_holders_handler filter_property_value_holders) + web::json::value modify_rebuildable_block(nmos::resources& resources, object_properties_map& object_properties_holder_map, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& block_object_properties_holder, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, nmos::filter_property_holders_handler filter_property_holders) { auto object_properties_set_validations = web::json::value::array(); // Find object_properties_holder for resource // the calling function guarantees that this target role path is in the object_properties_holders // and property_holder for block members exists - const auto& block_members_properties_holder = nmos::get_property_value_holder(block_object_properties_holder, nmos::nc_block_members_property_id); + const auto& block_members_properties_holder = nmos::get_property_holder(block_object_properties_holder, nmos::nc_block_members_property_id); const auto& restore_members = nmos::fields::nc::value(block_members_properties_holder); const auto& reference_members = nmos::fields::nc::members(resource.data); @@ -221,7 +221,7 @@ namespace nmos auto added_object_notices = web::json::value::array(); - const auto& oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder->second, nmos::nc_object_oid_property_id); + const auto& oid_property_holder = nmos::get_property_holder(child_object_properties_holder->second, nmos::nc_object_oid_property_id); oid = oid_property_holder == web::json::value::null() ? oid : nmos::fields::nc::value(oid_property_holder).as_integer(); if (oid_property_holder != web::json::value::null() && oid != nmos::fields::nc::value(oid_property_holder).as_integer()) @@ -269,7 +269,7 @@ namespace nmos // The values in the block member object properties holder will take precidence over the block member descriptor values // If the block member values are inconsistant then warn - description and user label values can be independant - const auto& role_property_holder = nmos::get_property_value_holder(child_object_properties_holder->second, nmos::nc_object_role_property_id); + const auto& role_property_holder = nmos::get_property_holder(child_object_properties_holder->second, nmos::nc_object_role_property_id); role = role_property_holder == web::json::value::null() ? role : nmos::fields::nc::value(role_property_holder).as_string(); if (role_property_holder != web::json::value::null() && role != nmos::fields::nc::value(role_property_holder).as_string()) @@ -286,7 +286,7 @@ namespace nmos const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); web::json::push_back(block_notices, notice); } - const auto& owner_property_holder = nmos::get_property_value_holder(child_object_properties_holder->second, nmos::nc_object_owner_property_id); + const auto& owner_property_holder = nmos::get_property_holder(child_object_properties_holder->second, nmos::nc_object_owner_property_id); if (owner_property_holder != web::json::value::null() && block_oid != nmos::fields::nc::value(owner_property_holder).as_integer()) { utility::stringstream_t ss; @@ -294,7 +294,7 @@ namespace nmos const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); web::json::push_back(added_object_notices, notice); } - const auto& constant_oid_property_holder = nmos::get_property_value_holder(child_object_properties_holder->second, nmos::nc_object_constant_oid_property_id); + const auto& constant_oid_property_holder = nmos::get_property_holder(child_object_properties_holder->second, nmos::nc_object_constant_oid_property_id); constant_oid = constant_oid_property_holder == web::json::value::null() ? constant_oid : nmos::fields::nc::value(constant_oid_property_holder).as_bool(); if (constant_oid_property_holder != web::json::value::null() && constant_oid != nmos::fields::nc::value(constant_oid_property_holder).as_bool()) @@ -305,7 +305,7 @@ namespace nmos web::json::push_back(block_notices, notice); } - const auto& user_label_property_holder = nmos::get_property_value_holder(child_object_properties_holder->second, nmos::nc_object_user_label_property_id); + const auto& user_label_property_holder = nmos::get_property_holder(child_object_properties_holder->second, nmos::nc_object_user_label_property_id); const auto& user_label = (user_label_property_holder == web::json::value::null()) ? block_member_user_label : nmos::fields::nc::value(user_label_property_holder).as_string(); auto object_properties_set_validation = add_device_model_object(child_object_properties_holder->second, oid, owner, role, user_label, validate); @@ -407,22 +407,22 @@ namespace nmos return role_path.as_array(); } - web::json::value get_property_value_holder(const web::json::value& object_properties_holder, const nmos::nc_property_id& property_id) + web::json::value get_property_holder(const web::json::value& object_properties_holder, const nmos::nc_property_id& property_id) { - const auto& filtered_property_value_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) - | boost::adaptors::filtered([&property_id](const web::json::value& property_value_holder) + const auto& filtered_property_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) + | boost::adaptors::filtered([&property_id](const web::json::value& property_holder) { - return property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value_holder)); + return property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)); }) ); // There should only be a single property holder for the members - if (filtered_property_value_holders.size() != 1) + if (filtered_property_holders.size() != 1) { // Error return web::json::value::null(); } - return *filtered_property_value_holders.begin(); + return *filtered_property_holders.begin(); } // Check to see if root_role_path is root of role_path @@ -452,9 +452,9 @@ namespace nmos return false; } const auto& block_members_properties_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) - | boost::adaptors::filtered([](const web::json::value& property_value_holder) + | boost::adaptors::filtered([](const web::json::value& property_holder) { - return nmos::nc_property_id(2, 2) == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value_holder)); + return nmos::nc_property_id(2, 2) == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)); }) ); // There should only be a single property holder for the members @@ -507,7 +507,7 @@ namespace nmos return web::json::value_from_elements(target_object_properties_holders).as_array(); } - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) { auto object_properties_set_validation_values = web::json::value::array(); @@ -579,7 +579,7 @@ namespace nmos { // Process this block to add / remove device model objects as members of this block // the object properties holder for any added objects will be erased from the object_properties_holder_map to avoid double processing - const auto& child_object_properties_set_validations = details::modify_rebuildable_block(resources, object_properties_holder_map, *r, role_path, object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object, filter_property_value_holders); + const auto& child_object_properties_set_validations = details::modify_rebuildable_block(resources, object_properties_holder_map, *r, role_path, object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object, filter_property_holders); for (const auto& validation_values : child_object_properties_set_validations.as_array()) { web::json::push_back(object_properties_set_validation_values, validation_values); @@ -595,7 +595,7 @@ namespace nmos } else { - const auto object_properties_set_validation = details::modify_device_model_object(resources, *r, role_path, object_properties_holder, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders); + const auto object_properties_set_validation = details::modify_device_model_object(resources, *r, role_path, object_properties_holder, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); } diff --git a/Development/nmos/configuration_utils.h b/Development/nmos/configuration_utils.h index 13847d67c..81bbaaffa 100644 --- a/Development/nmos/configuration_utils.h +++ b/Development/nmos/configuration_utils.h @@ -20,9 +20,9 @@ namespace nmos // Get object_properties_holder for specified target_role_path web::json::array get_object_properties_holder(const web::json::array& object_properties_holders, const web::json::array& target_role_path); - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_value_holders_handler filter_property_value_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); - web::json::value get_property_value_holder(const web::json::value& object_properties_holder, const nmos::nc_property_id& property_id); + web::json::value get_property_holder(const web::json::value& object_properties_holder, const nmos::nc_property_id& property_id); } #endif diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 0c051fb97..3d7518d9c 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -844,7 +844,7 @@ namespace nmos return data; } - web::json::value make_nc_bulk_values_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders) + web::json::value make_nc_bulk_properties_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders) { using web::json::value_of; @@ -856,7 +856,7 @@ namespace nmos } // TODO: add link - web::json::value make_nc_property_value_holder(const nc_property_id& property_id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, const web::json::value& property_value) + web::json::value make_nc_property_holder(const nc_property_id& property_id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, const web::json::value& property_value) { using web::json::value; using web::json::value_of; @@ -871,7 +871,7 @@ namespace nmos } // TODO: add link - web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_value_holders, const web::json::array& dependency_paths, const web::json::array& allowed_members_classes, bool is_rebuildable) + web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_holders, const web::json::array& dependency_paths, const web::json::array& allowed_members_classes, bool is_rebuildable) { using web::json::value_of; @@ -879,7 +879,7 @@ namespace nmos { nmos::fields::nc::path, web::json::value_from_elements(role_path)}, { nmos::fields::nc::dependency_paths, web::json::value_from_elements(dependency_paths)}, { nmos::fields::nc::allowed_members_classes, web::json::value_from_elements(allowed_members_classes)}, - { nmos::fields::nc::values, web::json::value_from_elements(property_value_holders)}, + { nmos::fields::nc::values, web::json::value_from_elements(property_holders)}, { nmos::fields::nc::is_rebuildable, is_rebuildable} }, true ); @@ -1331,11 +1331,11 @@ namespace nmos auto parameters = value::array(); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The target role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will return properties on specified path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get bulk object properties by given path"), nc_bulk_properties_manager_get_properties_by_path_method_id, U("GetPropertiesByPath"), U("NcMethodResultBulkValuesHolder"), parameters, false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get bulk object properties by given path"), nc_bulk_properties_manager_get_properties_by_path_method_id, U("GetPropertiesByPath"), U("NcMethodResultBulkPropertiesHolder"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkValuesHolder"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkPropertiesHolder"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Defines the restore mode to be applied"), nmos::fields::nc::restore_mode, U("NcRestoreMode"), false, false, value::null())); @@ -1343,7 +1343,7 @@ namespace nmos } { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkValuesHolder"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkPropertiesHolder"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Defines the restore mode to be applied"), nmos::fields::nc::restore_mode, U("NcRestoreMode"), false, false, value::null())); @@ -2204,7 +2204,7 @@ namespace nmos return details::make_nc_datatype_descriptor_enum(U("Restore mode enumeration"), U("NcRestoreMode"), items, value::null()); } // TODO: add link - web::json::value make_nc_property_value_holder_datatype() + web::json::value make_nc_property_holder_datatype() { using web::json::value; @@ -2215,7 +2215,7 @@ namespace nmos web::json::push_back(fields, details::make_nc_field_descriptor(U("Is the property ReadOnly?"), nmos::fields::nc::is_read_only, U("NcBoolean"), false, false, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Property value holder descriptor"), U("NcPropertyValueHolder"), fields, value::null()); + return details::make_nc_datatype_descriptor_struct(U("Property holder descriptor"), U("NcPropertyHolder"), fields, value::null()); } // TODO: add link web::json::value make_nc_object_properties_holder_datatype() @@ -2226,13 +2226,13 @@ namespace nmos web::json::push_back(fields, details::make_nc_field_descriptor(U("Object role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Sequence of role paths which are a dependency for this object"), nmos::fields::nc::dependency_paths, U("NcRolePath"), false, true, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Sequence of class ids allowed as members of the block"), nmos::fields::nc::allowed_members_classes, U("NcClassId"), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Object properties values"), nmos::fields::nc::values, U("NcPropertyValueHolder"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Object properties"), nmos::fields::nc::values, U("NcPropertyHolder"), false, true, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Describes if the object is rebuildable"), nmos::fields::nc::is_rebuildable, U("NcBoolean"), false, false, value::null())); return details::make_nc_datatype_descriptor_struct(U("Object properties holder descriptor"), U("NcObjectPropertiesHolder"), fields, value::null()); } // TODO: add link - web::json::value make_nc_bulk_values_holder_datatype() + web::json::value make_nc_bulk_properties_holder_datatype() { using web::json::value; @@ -2240,7 +2240,7 @@ namespace nmos web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional vendor specific fingerprinting mechanism used for validation purposes"), nmos::fields::nc::validation_fingerprint, U("NcString"), true, false, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Values by rolePath"), nmos::fields::nc::values, U("NcObjectPropertiesHolder"), false, true, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Bulk values holder descriptor"), U("NcBulkValuesHolder"), fields, value::null()); + return details::make_nc_datatype_descriptor_struct(U("Bulk properties holder descriptor"), U("NcBulkPropertiesHolder"), fields, value::null()); } // TODO: add link web::json::value make_nc_restore_validation_status_datatype() @@ -2291,14 +2291,14 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(U("Object properties set validation descriptor"), U("NcObjectPropertiesSetValidation"), fields, value::null()); } // TODO: add link - web::json::value make_nc_method_result_bulk_values_holder_datatype() + web::json::value make_nc_method_result_bulk_properties_holder_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Bulk values holder value"), nmos::fields::nc::value, U("NcBulkValuesHolder"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Bulk properties holder value"), nmos::fields::nc::value, U("NcBulkPropertiesHolder"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Method result containing bulk values holder descriptor"), U("NcMethodResultBulkValuesHolder"), fields, U("NcMethodResult"), value::null()); + return details::make_nc_datatype_descriptor_struct(U("Method result containing bulk properties holder descriptor"), U("NcMethodResultBulkPropertiesHolder"), fields, U("NcMethodResult"), value::null()); } // TODO: add link web::json::value make_nc_method_result_object_properties_set_validation_datatype() diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 9d313da72..01b430420 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -196,13 +196,13 @@ namespace nmos web::json::value make_nc_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); // TODO: add link - web::json::value make_nc_bulk_values_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders); + web::json::value make_nc_bulk_properties_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders); // TODO: add link - web::json::value make_nc_property_value_holder(const nc_property_id& property_id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, const web::json::value& property_value); + web::json::value make_nc_property_holder(const nc_property_id& property_id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, const web::json::value& property_value); // TODO: add link - web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_value_holders, const web::json::array& dependency_paths, const web::json::array& allowed_members_classes, bool is_rebuildable); + web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_holders, const web::json::array& dependency_paths, const web::json::array& allowed_members_classes, bool is_rebuildable); // TODO: add link web::json::value make_nc_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message); @@ -458,11 +458,11 @@ namespace nmos // web::json::value make_nc_restore_mode_datatype(); // - web::json::value make_nc_property_value_holder_datatype(); + web::json::value make_nc_property_holder_datatype(); // web::json::value make_nc_object_properties_holder_datatype(); // - web::json::value make_nc_bulk_values_holder_datatype(); + web::json::value make_nc_bulk_properties_holder_datatype(); // web::json::value make_nc_restore_validation_status_datatype(); // @@ -472,7 +472,7 @@ namespace nmos // web::json::value make_nc_object_properties_set_validation_datatype(); // - web::json::value make_nc_method_result_bulk_values_holder_datatype(); + web::json::value make_nc_method_result_bulk_properties_holder_datatype(); // web::json::value make_nc_method_result_object_properties_set_validation_datatype(); } diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 99df2dcd2..c1e756603 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -189,9 +189,9 @@ namespace nmos return nmos::get_properties_by_path(resources, resource, recurse, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); }; } - nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, filter_property_value_holders_handler filter_property_value_holders, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) + nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, filter_property_holders_handler filter_property_holders, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) { - return [&get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [&get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -203,9 +203,9 @@ namespace nmos } auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); - if (filter_property_value_holders && remove_device_model_object && add_device_model_object) + if (filter_property_holders && remove_device_model_object && add_device_model_object) { - result = validate_set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + result = validate_set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -216,9 +216,9 @@ namespace nmos return result; }; } - nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, filter_property_value_holders_handler filter_property_value_holders, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) + nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, filter_property_holders_handler filter_property_holders, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) { - return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -230,9 +230,9 @@ namespace nmos } auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("callbacks not implemented")); - if (filter_property_value_holders && remove_device_model_object && add_device_model_object) + if (filter_property_holders && remove_device_model_object && add_device_model_object) { - result = set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + result = set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -245,7 +245,7 @@ namespace nmos } } - control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, filter_property_value_holders_handler filter_property_value_holders, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) + control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, filter_property_holders_handler filter_property_holders, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) { auto to_vector = [](const web::json::value& data) { @@ -384,8 +384,8 @@ namespace nmos to_methods_vector(make_nc_bulk_properties_manager_methods(), { { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this))}, - { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), filter_property_value_holders, remove_device_model_object, add_device_model_object) }, - { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), filter_property_value_holders, remove_device_model_object, add_device_model_object) } + { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), filter_property_holders, remove_device_model_object, add_device_model_object) }, + { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), filter_property_holders, remove_device_model_object, add_device_model_object) } }), to_vector(make_nc_bulk_properties_manager_events())) } }; @@ -470,14 +470,14 @@ namespace nmos // Device configuration feature set // TODO: add link { U("NcRestoreMode"), {make_nc_restore_mode_datatype()} }, - { U("NcPropertyValueHolder"), {make_nc_property_value_holder_datatype()}}, + { U("NcPropertyHolder"), {make_nc_property_holder_datatype()}}, { U("NcObjectPropertiesHolder"), {make_nc_object_properties_holder_datatype()}}, - { U("NcBulkValuesHolder"), {make_nc_bulk_values_holder_datatype()}}, + { U("NcBulkPropertiesHolder"), {make_nc_bulk_properties_holder_datatype()}}, { U("NcRestoreValidationStatus"), {make_nc_restore_validation_status_datatype()}}, { U("NcPropertyRestoreNoticeType"), {make_nc_property_restore_notice_type_datatype()}}, { U("NcPropertyRestoreNotice"), {make_nc_property_restore_notice_datatype()}}, { U("NcObjectPropertiesSetValidation"), {make_nc_object_properties_set_validation_datatype()}}, - { U("NcMethodResultBulkValuesHolder"), {make_nc_method_result_bulk_values_holder_datatype()}}, + { U("NcMethodResultBulkPropertiesHolder"), {make_nc_method_result_bulk_properties_holder_datatype()}}, { U("NcMethodResultObjectPropertiesSetValidation"), {make_nc_method_result_object_properties_set_validation_datatype()}} }; } diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index 29b0dab2b..d8a75bded 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -59,7 +59,7 @@ namespace nmos nmos::read_lock read_lock() const { return nmos::read_lock{ mutex }; } nmos::write_lock write_lock() const { return nmos::write_lock{ mutex }; } - control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, filter_property_value_holders_handler filter_property_value_holders = nullptr, remove_device_model_object_handler remove_device_model_object = nullptr, add_device_model_object_handler add_device_model_object = nullptr); + control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, filter_property_holders_handler filter_property_holders = nullptr, remove_device_model_object_handler remove_device_model_object = nullptr, add_device_model_object_handler add_device_model_object = nullptr); // insert control class descriptor, false if class descriptor already inserted bool insert(const experimental::control_class_descriptor& control_class_descriptor); // erase control class of the given class id, false if the required class not found diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index 8f70071fb..11de4eb6c 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -339,7 +339,7 @@ namespace nmos const web::json::field_as_array values{ U("values") }; const web::json::field_as_string validation_fingerprint{ U("validationFingerprint") }; const web::json::field_as_value status_message{ U("statusMessage") }; - const web::json::field_as_value data_set{ U("dataSet") }; // NcBulkValuesHolder + const web::json::field_as_value data_set{ U("dataSet") }; // NcBulkPropertiesHolder const web::json::field_as_bool is_rebuildable{ U("isRebuildable") }; const web::json::field_as_integer notice_type{ U("noticeType") }; const web::json::field_as_string notice_message{ U("noticeMessage") }; diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index d8cd1642d..3c53de8fc 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -77,7 +77,7 @@ namespace nmos // Configure the Configuration API - node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.filter_property_value_holders, node_implementation.remove_device_model_object, node_implementation.add_device_model_object, node_implementation.control_protocol_property_changed, gate)); + node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.filter_property_holders, node_implementation.remove_device_model_object, node_implementation.add_device_model_object, node_implementation.control_protocol_property_changed, gate)); const auto& events_ws_port = nmos::fields::events_ws_port(node_model.settings); auto& events_ws_api = node_server.ws_handlers[{ {}, nmos::fields::events_ws_port(node_model.settings) }]; diff --git a/Development/nmos/node_server.h b/Development/nmos/node_server.h index 30d764cb5..13faacdcd 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -28,7 +28,7 @@ namespace nmos // underlying implementation into the server instance for the NMOS Node struct node_implementation { - node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::filter_property_value_holders_handler filter_property_value_holders, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) + node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::filter_property_holders_handler filter_property_holders, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) : load_server_certificates(std::move(load_server_certificates)) , load_dh_param(std::move(load_dh_param)) , load_ca_certificates(std::move(load_ca_certificates)) @@ -51,7 +51,7 @@ namespace nmos , get_control_protocol_datatype_descriptor(std::move(get_control_protocol_datatype_descriptor)) , get_control_protocol_method_descriptor(std::move(get_control_protocol_method_descriptor)) , control_protocol_property_changed(std::move(control_protocol_property_changed)) - , filter_property_value_holders(std::move(filter_property_value_holders)) + , filter_property_holders(std::move(filter_property_holders)) , remove_device_model_object(std::move(remove_device_model_object)) , add_device_model_object(std::move(add_device_model_object)) {} @@ -86,7 +86,7 @@ namespace nmos node_implementation& on_get_control_datatype_descriptor(nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { this->get_control_protocol_datatype_descriptor = std::move(get_control_protocol_datatype_descriptor); return *this; } node_implementation& on_get_control_protocol_method_descriptor(nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor) { this->get_control_protocol_method_descriptor = std::move(get_control_protocol_method_descriptor); return *this; } node_implementation& on_control_protocol_property_changed(nmos::control_protocol_property_changed_handler control_protocol_property_changed) { this->control_protocol_property_changed = std::move(control_protocol_property_changed); return *this; } - node_implementation& on_filter_property_value_holders(nmos::filter_property_value_holders_handler filter_property_value_holders) { this->filter_property_value_holders = std::move(filter_property_value_holders); return *this; } + node_implementation& on_filter_property_holders(nmos::filter_property_holders_handler filter_property_holders) { this->filter_property_holders = std::move(filter_property_holders); return *this; } node_implementation& on_remove_device_model_object(nmos::remove_device_model_object_handler remove_device_model_object) { this->remove_device_model_object = std::move(remove_device_model_object); return *this; } node_implementation& on_add_device_model_object(nmos::add_device_model_object_handler add_device_model_object) { this->add_device_model_object = std::move(add_device_model_object); return *this; } @@ -133,7 +133,7 @@ namespace nmos nmos::control_protocol_property_changed_handler control_protocol_property_changed; // Device Configuration handlers - nmos::filter_property_value_holders_handler filter_property_value_holders; + nmos::filter_property_holders_handler filter_property_holders; nmos::remove_device_model_object_handler remove_device_model_object; nmos::add_device_model_object_handler add_device_model_object; }; diff --git a/Development/nmos/test/configuration_methods_test.cpp b/Development/nmos/test/configuration_methods_test.cpp index b9c8a4b68..bfa8f1b42 100644 --- a/Development/nmos/test/configuration_methods_test.cpp +++ b/Development/nmos/test/configuration_methods_test.cpp @@ -61,8 +61,8 @@ BST_TEST_CASE(testGetPropertiesByPath) BST_REQUIRE_EQUAL(nmos::nc_method_status::ok, nmos::fields::nc::status(method_result)); - const auto& bulk_values_holder = nmos::fields::nc::value(method_result); - const auto& object_properties_holders = nmos::fields::nc::values(bulk_values_holder); + const auto& bulk_properties_holder = nmos::fields::nc::value(method_result); + const auto& object_properties_holders = nmos::fields::nc::values(bulk_properties_holder); BST_REQUIRE_EQUAL(5, object_properties_holders.size()); } @@ -73,8 +73,8 @@ BST_TEST_CASE(testGetPropertiesByPath) BST_REQUIRE_EQUAL(nmos::nc_method_status::ok, nmos::fields::nc::status(method_result)); - const auto& bulk_values_holder = nmos::fields::nc::value(method_result); - const auto& object_properties_holders = nmos::fields::nc::values(bulk_values_holder); + const auto& bulk_properties_holder = nmos::fields::nc::value(method_result); + const auto& object_properties_holders = nmos::fields::nc::values(bulk_properties_holder); BST_REQUIRE_EQUAL(3, object_properties_holders.size()); } @@ -85,8 +85,8 @@ BST_TEST_CASE(testGetPropertiesByPath) BST_REQUIRE_EQUAL(nmos::nc_method_status::ok, nmos::fields::nc::status(method_result)); - const auto& bulk_values_holder = nmos::fields::nc::value(method_result); - const auto& object_properties_holders = nmos::fields::nc::values(bulk_values_holder); + const auto& bulk_properties_holder = nmos::fields::nc::value(method_result); + const auto& object_properties_holders = nmos::fields::nc::values(bulk_properties_holder); BST_REQUIRE_EQUAL(1, object_properties_holders.size()); } diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 813938f52..1cd93f259 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -77,7 +77,7 @@ BST_TEST_CASE(testIsBlockModified) // Members unchanged { - auto property_value_holders = value::array(); + auto property_holders = value::array(); auto members = value::array(); const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); @@ -90,32 +90,32 @@ BST_TEST_CASE(testIsBlockModified) push_back(members, block_member_descriptor); } const nmos::nc_property_id property_id(2, 2); // block members - const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); - web::json::push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto property_holder = nmos::details::make_nc_property_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_holders, property_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(!nmos::is_block_modified(receivers, object_properties_holder)); } // Changed number of members { - auto property_value_holders = value::array(); + auto property_holders = value::array(); auto members = value::array(); const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); const auto block_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, class_id, U("label"), receiver_block_oid); push_back(members, block_descriptor); const nmos::nc_property_id property_id(2, 2); // block members - const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); - web::json::push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto property_holder = nmos::details::make_nc_property_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_holders, property_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } // Changed oids { - auto property_value_holders = value::array(); + auto property_holders = value::array(); auto members = value::array(); const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); @@ -128,16 +128,16 @@ BST_TEST_CASE(testIsBlockModified) push_back(members, block_member_descriptor); } const nmos::nc_property_id property_id(2, 2); // block members - const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); - web::json::push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto property_holder = nmos::details::make_nc_property_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_holders, property_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } // Changed roles { - auto property_value_holders = value::array(); + auto property_holders = value::array(); auto members = value::array(); const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); @@ -150,16 +150,16 @@ BST_TEST_CASE(testIsBlockModified) push_back(members, block_member_descriptor); } const nmos::nc_property_id property_id(2, 2); // block members - const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); - web::json::push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto property_holder = nmos::details::make_nc_property_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_holders, property_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } // Changed class id { - auto property_value_holders = value::array(); + auto property_holders = value::array(); auto members = value::array(); const auto class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 1 }); @@ -172,16 +172,16 @@ BST_TEST_CASE(testIsBlockModified) push_back(members, block_member_descriptor); } const nmos::nc_property_id property_id(2, 2); // block members - const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); - web::json::push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto property_holder = nmos::details::make_nc_property_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_holders, property_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } // Changed owner oid { - auto property_value_holders = value::array(); + auto property_holders = value::array(); auto members = value::array(); const auto class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 1 }); @@ -194,16 +194,16 @@ BST_TEST_CASE(testIsBlockModified) push_back(members, block_member_descriptor); } const nmos::nc_property_id property_id(2, 2); // block members - const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); - web::json::push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto property_holder = nmos::details::make_nc_property_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_holders, property_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } // Changed constant oid { - auto property_value_holders = value::array(); + auto property_holders = value::array(); auto members = value::array(); const auto class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 1 }); @@ -216,9 +216,9 @@ BST_TEST_CASE(testIsBlockModified) push_back(members, block_member_descriptor); } const nmos::nc_property_id property_id(2, 2); // block members - const auto property_value_holder = nmos::details::make_nc_property_value_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); - web::json::push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto property_holder = nmos::details::make_nc_property_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + web::json::push_back(property_holders, property_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -235,26 +235,26 @@ BST_TEST_CASE(testGetObjectPropertiesHolder) { const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - auto property_value_holders = value::array(); - const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); - push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + auto property_holders = value::array(); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + push_back(property_holders, property_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); } { const auto role_path = value_of({ U("root"), U("receivers"), U("mon2") }); - auto property_value_holders = value::array(); - const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); - push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + auto property_holders = value::array(); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + push_back(property_holders, property_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); } { const auto role_path = value_of({ U("root"), U("senders"), U("mon1") }); - auto property_value_holders = value::array(); - const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); - push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + auto property_holders = value::array(); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + push_back(property_holders, property_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); } @@ -377,21 +377,21 @@ BST_TEST_CASE(testApplyBackupDataSet) insert_resource(resources, std::move(monitor1)); insert_resource(resources, std::move(monitor2)); - bool filter_property_value_holders_called = false; + bool filter_property_holders_called = false; bool remove_device_model_object_called = false; bool add_device_model_object_called = false; // callback stubs - nmos::filter_property_value_holders_handler filter_property_value_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::filter_property_holders_handler filter_property_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { - filter_property_value_holders_called = true; - auto modifiable_property_value_holders = value::array(); + filter_property_holders_called = true; + auto modifiable_property_holders = value::array(); for (const auto& property_value : property_values) { - web::json::push_back(modifiable_property_value_holders, property_value); + web::json::push_back(modifiable_property_holders, property_value); } - return modifiable_property_value_holders.as_array(); + return modifiable_property_holders.as_array(); }; nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::nc_oid reference_oid, bool validate) @@ -414,10 +414,10 @@ BST_TEST_CASE(testApplyBackupDataSet) // Create Object Properties Holder auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - auto property_value_holders = value::array(); - const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); - push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + auto property_holders = value::array(); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + push_back(property_holders, property_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); const auto target_role_path = value_of({ U("root"), U("receivers")}); bool recurse = true; @@ -425,7 +425,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -436,26 +436,26 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); // not expecting callbacks to be invoked as no read only properties, or rebuildable blocks modified - BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!filter_property_holders_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } { - // Check filter_property_value_holders_handler is called when changing a read only property of rebuildable object in Rebuild mode + // Check filter_property_holders_handler is called when changing a read only property of rebuildable object in Rebuild mode // - filter_property_value_holders_called = false; + filter_property_holders_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - auto property_value_holders = value::array(); + auto property_holders = value::array(); const nmos::nc_property_id property_id(2, 1); // This is a read only property - const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); - push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); + push_back(property_holders, property_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -464,7 +464,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -474,28 +474,28 @@ BST_TEST_CASE(testApplyBackupDataSet) // make sure the validation status propagates from the callback BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); - // expecting callback to filter_property_value_holders_called + // expecting callback to filter_property_holders_called // but not to modify_rebuildable_block_called - BST_CHECK(filter_property_value_holders_called); + BST_CHECK(filter_property_holders_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } { // Check error generated when attempting to change a read only property of non-rebuidable object in Rebuild mode // - filter_property_value_holders_called = false; + filter_property_holders_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon2") }); - auto property_value_holders = value::array(); + auto property_holders = value::array(); const nmos::nc_property_id property_id(2, 1); // This is a read only property - const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); - push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); + push_back(property_holders, property_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -504,7 +504,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -524,16 +524,16 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); - // expecting callback to filter_property_value_holders_called + // expecting callback to filter_property_holders_called // but not to modify_rebuildable_block_called - BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!filter_property_holders_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } { // Check an error is caused by trying to modify a read only property in Modify mode // - filter_property_value_holders_called = false; + filter_property_holders_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; @@ -541,13 +541,13 @@ BST_TEST_CASE(testApplyBackupDataSet) // Create Object Properties Holder auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - auto property_value_holders = value::array(); + auto property_holders = value::array(); const nmos::nc_property_id property_id(2, 1); // This is a read only property - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), true, value("change this value"))); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), true, value("change this value"))); // This is a writable property - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false)); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false)); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -556,7 +556,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -577,33 +577,33 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); - BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!filter_property_holders_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } { // Check remove_device_model_object_called is called when trying to modify a rebuildable block // - filter_property_value_holders_called = false; + filter_property_holders_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers") }); - auto property_value_holders = value::array(); + auto property_holders = value::array(); auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -613,14 +613,14 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); - BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!filter_property_holders_called); BST_CHECK(remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } { // Check add_device_model_object_called is called when trying to modify a rebuildable block // - filter_property_value_holders_called = false; + filter_property_holders_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; @@ -629,26 +629,26 @@ BST_TEST_CASE(testApplyBackupDataSet) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers") }); { - auto property_value_holders = value::array(); + auto property_holders = value::array(); auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { - auto property_value_holders = value::array(); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + auto property_holders = value::array(); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { - auto property_value_holders = value::array(); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + auto property_holders = value::array(); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -656,7 +656,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors BST_REQUIRE_EQUAL(3, output.as_array().size()); @@ -684,57 +684,57 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); } - BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!filter_property_holders_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(add_device_model_object_called); } { // Check that role paths outside of the scope of the target role path are errored // - filter_property_value_holders_called = false; + filter_property_holders_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("other_receivers") }); - auto property_value_holders = value::array(); + auto property_holders = value::array(); auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation no object_properties_holders as not in the restore scope BST_REQUIRE_EQUAL(0, output.as_array().size()); - BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!filter_property_holders_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } { - // Mixture of filter_property_value_holders_handler and errors in Rebuild mode + // Mixture of filter_property_holders_handler and errors in Rebuild mode // - filter_property_value_holders_called = false; + filter_property_holders_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - auto property_value_holders = value::array(); + auto property_holders = value::array(); const nmos::nc_property_id property_id(2, 1); // This is a read only property - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value"))); //read only - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcString"), false, false)); // error in data type - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value"))); //read only + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcString"), false, false)); // error in data type + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -742,7 +742,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -761,26 +761,26 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); - // expecting callback to filter_property_value_holders_called + // expecting callback to filter_property_holders_called // but not to modify_rebuildable_block_called - BST_CHECK(filter_property_value_holders_called); + BST_CHECK(filter_property_holders_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } { - // Incorrect property name in property value holders + // Incorrect property name in property holders // - filter_property_value_holders_called = false; + filter_property_holders_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - auto property_value_holders = value::array(); + auto property_holders = value::array(); // This is a read only property - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("wrong_property_name"), U("NcString"), false, value("change this value"))); //read only - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(3, 2), U("wrong_property_name"), U("NcString"), false, value("change this value"))); //read only + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -788,7 +788,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -807,26 +807,26 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); - // expecting callback to filter_property_value_holders_called + // expecting callback to filter_property_holders_called // but not to modify_rebuildable_block_called - BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!filter_property_holders_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } { - // Incorrect property type in property value holders + // Incorrect property type in property holders // - filter_property_value_holders_called = false; + filter_property_holders_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - auto property_value_holders = value::array(); + auto property_holders = value::array(); // This is a read only property - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("wrong_data_type"), false, value("change this value"))); //read only - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("wrong_data_type"), false, value("change this value"))); //read only + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -834,7 +834,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -853,9 +853,9 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); - // expecting callback to filter_property_value_holders_called + // expecting callback to filter_property_holders_called // but not to modify_rebuildable_block_called - BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!filter_property_holders_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } @@ -903,7 +903,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) insert_resource(resources, std::move(monitor2)); // undefined callback stubs - nmos::filter_property_value_holders_handler filter_property_value_holders; + nmos::filter_property_holders_handler filter_property_holders; nmos::remove_device_model_object_handler remove_device_model_object; nmos::add_device_model_object_handler add_device_model_object; @@ -913,10 +913,10 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) // Create Object Properties Holder auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - auto property_value_holders = value::array(); - const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false); - push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + auto property_holders = value::array(); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false); + push_back(property_holders, property_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -924,7 +924,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -940,10 +940,10 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) // Create Object Properties Holder auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - auto property_value_holders = value::array(); - const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false); - push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + auto property_holders = value::array(); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false); + push_back(property_holders, property_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -951,7 +951,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -962,17 +962,17 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); } { - // Check undefined filter_property_value_holders_handler causes an unsupported mode error when attempting to modify a read only property in Rebuild mode + // Check undefined filter_property_holders_handler causes an unsupported mode error when attempting to modify a read only property in Rebuild mode // // Create Object Properties Holder auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - auto property_value_holders = value::array(); + auto property_holders = value::array(); const nmos::nc_property_id property_id(2, 1); // This is a read only property - const auto property_value_holder = nmos::details::make_nc_property_value_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); - push_back(property_value_holders, property_value_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); + push_back(property_holders, property_holder); + const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -981,7 +981,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -997,17 +997,17 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) // Create Object Properties Holder auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers") }); - auto property_value_holders = value::array(); + auto property_holders = value::array(); auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); { - auto property_value_holders = value::array(); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + auto property_holders = value::array(); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -1015,7 +1015,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holder BST_CHECK_EQUAL(2, output.as_array().size()); @@ -1079,21 +1079,21 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) insert_resource(resources, std::move(monitor1)); insert_resource(resources, std::move(monitor2)); - bool filter_property_value_holders_called = false; + bool filter_property_holders_called = false; bool remove_device_model_object_called = false; bool add_device_model_object_called = false; // callback stubs - nmos::filter_property_value_holders_handler filter_property_value_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::filter_property_holders_handler filter_property_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { - filter_property_value_holders_called = true; - auto modifiable_property_value_holders = value::array(); + filter_property_holders_called = true; + auto modifiable_property_holders = value::array(); for (const auto& property_value : property_values) { - web::json::push_back(modifiable_property_value_holders, property_value); + web::json::push_back(modifiable_property_holders, property_value); } - return modifiable_property_value_holders.as_array(); + return modifiable_property_holders.as_array(); }; nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::nc_oid reference_oid, bool validate) @@ -1113,7 +1113,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) { // Check new oid is generated for new device model object // - filter_property_value_holders_called = false; + filter_property_holders_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; @@ -1122,26 +1122,26 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers") }); { - auto property_value_holders = value::array(); + auto property_holders = value::array(); auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, false, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { - auto property_value_holders = value::array(); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + auto property_holders = value::array(); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { - auto property_value_holders = value::array(); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + auto property_holders = value::array(); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -1149,7 +1149,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors BST_REQUIRE_EQUAL(3, output.as_array().size()); @@ -1188,14 +1188,14 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) BST_CHECK_EQUAL(nmos::fields::nc::index(property_id), nmos::nc_object_oid_property_id.index); } - BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!filter_property_holders_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(add_device_model_object_called); } { // Handle constant oid clash // - filter_property_value_holders_called = false; + filter_property_holders_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; @@ -1203,26 +1203,26 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers") }); { - auto property_value_holders = value::array(); + auto property_holders = value::array(); auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { - auto property_value_holders = value::array(); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + auto property_holders = value::array(); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { - auto property_value_holders = value::array(); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + auto property_holders = value::array(); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -1230,7 +1230,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors BST_REQUIRE_EQUAL(3, output.as_array().size()); @@ -1255,7 +1255,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) BST_CHECK_EQUAL(nmos::nc_restore_validation_status::device_error, nmos::fields::nc::status(object_properties_set_validation)); } - BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!filter_property_holders_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } @@ -1304,21 +1304,21 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) insert_resource(resources, std::move(monitor1)); insert_resource(resources, std::move(monitor2)); - bool filter_property_value_holders_called = false; + bool filter_property_holders_called = false; bool remove_device_model_object_called = false; bool add_device_model_object_called = false; // callback stubs - nmos::filter_property_value_holders_handler filter_property_value_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::filter_property_holders_handler filter_property_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { - filter_property_value_holders_called = true; - auto modifiable_property_value_holders = value::array(); + filter_property_holders_called = true; + auto modifiable_property_holders = value::array(); for (const auto& property_value : property_values) { - web::json::push_back(modifiable_property_value_holders, property_value); + web::json::push_back(modifiable_property_holders, property_value); } - return modifiable_property_value_holders.as_array(); + return modifiable_property_holders.as_array(); }; nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::nc_oid reference_oid, bool validate) @@ -1339,26 +1339,26 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) { // Check remove_device_model_object_called error is handled when attempting to modify a rebuildable block // - filter_property_value_holders_called = false; + filter_property_holders_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers") }); - auto property_value_holders = value::array(); + auto property_holders = value::array(); auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -1373,31 +1373,31 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) BST_CHECK_EQUAL(nmos::fields::nc::level(property_id), nmos::nc_block_members_property_id.level); BST_CHECK_EQUAL(nmos::fields::nc::index(property_id), nmos::nc_block_members_property_id.index); - BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!filter_property_holders_called); BST_CHECK(remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } { // Check on remove_device_model_object_called error all other object properties holders are processed // - filter_property_value_holders_called = false; + filter_property_holders_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers") }); - auto property_value_holders = value::array(); + auto property_holders = value::array(); auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); { - auto property_value_holders = value::array(); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + auto property_holders = value::array(); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -1406,7 +1406,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders BST_CHECK_EQUAL(2, output.as_array().size()); @@ -1435,7 +1435,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) { // Check add_device_model_object_called error is handled when trying to modify a rebuildable block // - filter_property_value_holders_called = false; + filter_property_holders_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; @@ -1444,32 +1444,32 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers") }); { - auto property_value_holders = value::array(); + auto property_holders = value::array(); auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); { - auto property_value_holders = value::array(); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + auto property_holders = value::array(); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { - auto property_value_holders = value::array(); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + auto property_holders = value::array(); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { - auto property_value_holders = value::array(); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + auto property_holders = value::array(); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -1477,7 +1477,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors BST_CHECK_EQUAL(4, output.as_array().size()); @@ -1512,37 +1512,37 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); } - BST_CHECK(!filter_property_value_holders_called); + BST_CHECK(!filter_property_holders_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(add_device_model_object_called); } { // Check duplicate block object properties holders are handled // - filter_property_value_holders_called = false; + filter_property_holders_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers") }); - auto property_value_holders1 = value::array(); + auto property_holders1 = value::array(); auto members1 = value::array(); - auto property_value_holders2 = value::array(); + auto property_holders2 = value::array(); auto members2 = value::array(); push_back(members1, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_value_holders1, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members1)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders1.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders1, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members1)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders1.as_array(), value::array().as_array(), value::array().as_array(), false)); // duplicate push_back(members2, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_value_holders2, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members2)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_value_holders2.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders2, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members2)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders2.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); { - auto property_value_holders = value::array(); - push_back(property_value_holders, nmos::details::make_nc_property_value_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_value_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + auto property_holders = value::array(); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -1551,7 +1551,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_value_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders BST_CHECK_EQUAL(3, output.as_array().size()); From f86e93fbe644658d71afa22dac5aad3987296467 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 27 Jun 2025 17:30:05 +0100 Subject: [PATCH 193/250] Line up IS-04 schemas with specification --- Development/cmake/NmosCppLibraries.cmake | 9 +++++---- Development/nmos/configuration_api.cpp | 8 ++++---- Development/nmos/is14_schemas/is14_schemas.h | 8 ++++---- Development/nmos/json_schema.cpp | 16 ++++++++-------- Development/nmos/json_schema.h | 4 ++-- .../schemas/bulkProperties-get-response.json | 4 ++-- ...st.json => bulkProperties-patch-request.json} | 8 ++++++-- ...e.json => bulkProperties-patch-response.json} | 2 +- ...uest.json => bulkProperties-put-request.json} | 6 +++++- ...nse.json => bulkProperties-put-response.json} | 0 .../APIs/schemas/method-patch-request.json | 2 +- .../is-14/v1.0.x/APIs/schemas/ms05-error.json | 4 ++-- 12 files changed, 40 insertions(+), 31 deletions(-) rename Development/third_party/is-14/v1.0.x/APIs/schemas/{bulkProperties-validate-request.json => bulkProperties-patch-request.json} (60%) rename Development/third_party/is-14/v1.0.x/APIs/schemas/{bulkProperties-validate-response.json => bulkProperties-patch-response.json} (83%) rename Development/third_party/is-14/v1.0.x/APIs/schemas/{bulkProperties-set-request.json => bulkProperties-put-request.json} (74%) rename Development/third_party/is-14/v1.0.x/APIs/schemas/{bulkProperties-set-response.json => bulkProperties-put-response.json} (100%) diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index d52e91e25..bba5a120a 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -846,10 +846,11 @@ set(NMOS_IS14_V1_0_TAG v1.0.x) set(NMOS_IS14_V1_0_SCHEMAS_JSON third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/base.json third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-get-response.json - third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-set-request.json - third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-set-response.json - third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-validate-request.json - third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-validate-response.json + + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-patch-request.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-patch-response.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-put-request.json + third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/bulkProperties-put-response.json third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/descriptor-get.json third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/method-patch-request.json third_party/is-14/${NMOS_IS14_V1_0_TAG}/APIs/schemas/method-patch-response.json diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 03bcd5c59..5647da53d 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -129,8 +129,8 @@ namespace nmos boost::copy_range>(boost::range::join(boost::range::join(boost::range::join( is14_versions::all | boost::adaptors::transformed(experimental::make_configurationapi_method_patch_request_schema_uri), is14_versions::all | boost::adaptors::transformed(experimental::make_configurationapi_property_value_put_request_schema_uri)), - is14_versions::all | boost::adaptors::transformed(experimental::make_configurationapi_bulkProperties_validate_request_schema_uri)), - is14_versions::all | boost::adaptors::transformed(experimental::make_configurationapi_bulkProperties_set_request_schema_uri))) + is14_versions::all | boost::adaptors::transformed(experimental::make_configurationapi_bulkProperties_patch_request_schema_uri)), + is14_versions::all | boost::adaptors::transformed(experimental::make_configurationapi_bulkProperties_put_request_schema_uri))) }; return validator; } @@ -678,7 +678,7 @@ namespace nmos try { // Validate JSON syntax according to the schema - details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_validate_request_schema_uri(version)); + details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_patch_request_schema_uri(version)); const auto& arguments = nmos::fields::nc::arguments(body); bool recurse = nmos::fields::nc::recurse(arguments); @@ -742,7 +742,7 @@ namespace nmos try { // Validate JSON syntax according to the schema - details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_set_request_schema_uri(version)); + details::configurationapi_validator().validate(body, experimental::make_configurationapi_bulkProperties_put_request_schema_uri(version)); const auto& arguments = nmos::fields::nc::arguments(body); bool recurse = nmos::fields::nc::recurse(arguments); diff --git a/Development/nmos/is14_schemas/is14_schemas.h b/Development/nmos/is14_schemas/is14_schemas.h index 7a261fc04..6e4e50cb9 100644 --- a/Development/nmos/is14_schemas/is14_schemas.h +++ b/Development/nmos/is14_schemas/is14_schemas.h @@ -11,10 +11,10 @@ namespace nmos { extern const char* base; extern const char* bulkProperties_get_response; - extern const char* bulkProperties_set_request; - extern const char* bulkProperties_set_response; - extern const char* bulkProperties_validate_request; - extern const char* bulkProperties_validate_response; + extern const char* bulkProperties_put_request; + extern const char* bulkProperties_put_response; + extern const char* bulkProperties_patch_request; + extern const char* bulkProperties_patch_response; extern const char* descriptor_get; extern const char* method_patch_request; extern const char* method_patch_response; diff --git a/Development/nmos/json_schema.cpp b/Development/nmos/json_schema.cpp index 81bff63b4..3860509ca 100644 --- a/Development/nmos/json_schema.cpp +++ b/Development/nmos/json_schema.cpp @@ -186,8 +186,8 @@ namespace nmos using namespace nmos::is14_schemas::v1_0_x; const utility::string_t tag(_XPLATSTR("v1.0.x")); - const web::uri configurationapi_bulkProperties_set_request_schema_uri = make_schema_uri(tag, _XPLATSTR("bulkProperties-set-request.json")); - const web::uri configurationapi_bulkProperties_validate_request_schema_uri = make_schema_uri(tag, _XPLATSTR("bulkProperties-validate-request.json")); + const web::uri configurationapi_bulkProperties_put_request_schema_uri = make_schema_uri(tag, _XPLATSTR("bulkProperties-put-request.json")); + const web::uri configurationapi_bulkProperties_patch_request_schema_uri = make_schema_uri(tag, _XPLATSTR("bulkProperties-patch-request.json")); const web::uri configurationapi_method_patch_request_schema_uri = make_schema_uri(tag, _XPLATSTR("method-patch-request.json")); const web::uri configurationapi_property_value_put_request_schema_uri = make_schema_uri(tag, _XPLATSTR("property-value-put-request.json")); } @@ -420,8 +420,8 @@ namespace nmos return { // v1.0 - { make_schema_uri(v1_0::tag, _XPLATSTR("bulkProperties-set-request.json")), make_schema(v1_0::bulkProperties_set_request) }, - { make_schema_uri(v1_0::tag, _XPLATSTR("bulkProperties-validate-request.json")), make_schema(v1_0::bulkProperties_validate_request) }, + { make_schema_uri(v1_0::tag, _XPLATSTR("bulkProperties-put-request.json")), make_schema(v1_0::bulkProperties_put_request) }, + { make_schema_uri(v1_0::tag, _XPLATSTR("bulkProperties-patch-request.json")), make_schema(v1_0::bulkProperties_patch_request) }, { make_schema_uri(v1_0::tag, _XPLATSTR("method-patch-request.json")), make_schema(v1_0::method_patch_request) }, { make_schema_uri(v1_0::tag, _XPLATSTR("property-value-put-request.json")), make_schema(v1_0::property_value_put_request) } }; @@ -547,14 +547,14 @@ namespace nmos return is12_schemas::v1_0::controlprotocolapi_subscription_message_schema_uri; } - web::uri make_configurationapi_bulkProperties_set_request_schema_uri(const nmos::api_version& version) + web::uri make_configurationapi_bulkProperties_put_request_schema_uri(const nmos::api_version& version) { - return is14_schemas::v1_0::configurationapi_bulkProperties_set_request_schema_uri; + return is14_schemas::v1_0::configurationapi_bulkProperties_put_request_schema_uri; } - web::uri make_configurationapi_bulkProperties_validate_request_schema_uri(const nmos::api_version& version) + web::uri make_configurationapi_bulkProperties_patch_request_schema_uri(const nmos::api_version& version) { - return is14_schemas::v1_0::configurationapi_bulkProperties_validate_request_schema_uri; + return is14_schemas::v1_0::configurationapi_bulkProperties_patch_request_schema_uri; } web::uri make_configurationapi_method_patch_request_schema_uri(const nmos::api_version& version) diff --git a/Development/nmos/json_schema.h b/Development/nmos/json_schema.h index d1a06be60..28d6ea861 100644 --- a/Development/nmos/json_schema.h +++ b/Development/nmos/json_schema.h @@ -40,8 +40,8 @@ namespace nmos web::uri make_controlprotocolapi_command_message_schema_uri(const nmos::api_version& version); web::uri make_controlprotocolapi_subscription_message_schema_uri(const nmos::api_version& version); - web::uri make_configurationapi_bulkProperties_set_request_schema_uri(const nmos::api_version& version); - web::uri make_configurationapi_bulkProperties_validate_request_schema_uri(const nmos::api_version& version); + web::uri make_configurationapi_bulkProperties_put_request_schema_uri(const nmos::api_version& version); + web::uri make_configurationapi_bulkProperties_patch_request_schema_uri(const nmos::api_version& version); web::uri make_configurationapi_method_patch_request_schema_uri(const nmos::api_version& version); web::uri make_configurationapi_property_value_put_request_schema_uri(const nmos::api_version& version); diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-get-response.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-get-response.json index 9ddc05ead..698a1b358 100644 --- a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-get-response.json +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-get-response.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", - "description": "Returns a NcMethodResultBulkValuesHolder from a bulkProperties GET", - "title": "NcMethodResultBulkValuesHolder" + "description": "Returns a NcMethodResultBulkPropertiesHolder from a bulkProperties GET", + "title": "NcMethodResultBulkPropertiesHolder" } diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-request.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-patch-request.json similarity index 60% rename from Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-request.json rename to Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-patch-request.json index 7796c1ce1..bf711c5b3 100644 --- a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-request.json +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-patch-request.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", - "description": "OPTIONS request body for invoking ValidateSetPropertiesByPath method on NcBulkPropertiesManager", + "description": "PATCH request body for invoking ValidateSetPropertiesByPath method on NcBulkPropertiesManager", "title": "Bulk properties Validate request", "required": [ "arguments" @@ -13,10 +13,14 @@ "properties": { "dataSet": { "type": "object", - "description": "NcBulkValuesHolder datatype" + "description": "NcBulkPropertiesHolder datatype" }, "recurse": { "type": "boolean" + }, + "restoreMode": { + "type": "integer", + "description": "numeric representation of NcRestoreMode enum options" } } } diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-response.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-patch-response.json similarity index 83% rename from Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-response.json rename to Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-patch-response.json index 0a077c2f3..1642eb0d5 100644 --- a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-validate-response.json +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-patch-response.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", - "description": "Returns a NcMethodResultObjectPropertiesSetValidation from a bulkProperties OPTIONS", + "description": "Returns a NcMethodResultObjectPropertiesSetValidation from a bulkProperties PATCH", "title": "NcMethodResultObjectPropertiesSetValidation" } diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-request.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-put-request.json similarity index 74% rename from Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-request.json rename to Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-put-request.json index 4603b2275..75a1694e3 100644 --- a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-request.json +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-put-request.json @@ -13,10 +13,14 @@ "properties": { "dataSet": { "type": "object", - "description": "NcBulkValuesHolder datatype" + "description": "NcBulkPropertiesHolder datatype" }, "recurse": { "type": "boolean" + }, + "restoreMode": { + "type": "integer", + "description": "numeric representation of NcRestoreMode enum options" } } } diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-response.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-put-response.json similarity index 100% rename from Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-set-response.json rename to Development/third_party/is-14/v1.0.x/APIs/schemas/bulkProperties-put-response.json diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-request.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-request.json index 42e253515..ecf42c21b 100644 --- a/Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-request.json +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/method-patch-request.json @@ -9,7 +9,7 @@ "properties": { "arguments": { "type": "object", - "description": "Method arguments. Arguments are specified as nested properties inside this object and their types are dictated by the specific MS-05-02 model for the method targeted. For methods which do not have arguments defined the object MUST be an empty object." + "description": "Method arguments. Arguments are specified as nested properties inside this object and their types are dictated by the specific MS-05-02 model for the method targeted. For methods which do not have arguments defined the object is an empty object." } } } diff --git a/Development/third_party/is-14/v1.0.x/APIs/schemas/ms05-error.json b/Development/third_party/is-14/v1.0.x/APIs/schemas/ms05-error.json index 4eea45a37..a7996e064 100644 --- a/Development/third_party/is-14/v1.0.x/APIs/schemas/ms05-error.json +++ b/Development/third_party/is-14/v1.0.x/APIs/schemas/ms05-error.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", - "description": "NcMethodResultError", - "title": "NcMethodResultError" + "description": "Object of type NcMethodResultError or a type derived from NcMethodResultError", + "title": "Request error" } From d2d2c6d066ce483a739687356ac340e5f3393c72 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 27 Jun 2025 20:28:00 +0100 Subject: [PATCH 194/250] Update device configuration links --- .../nmos/control_protocol_resource.cpp | 35 ++++++++++--------- Development/nmos/control_protocol_resource.h | 35 +++++++++---------- .../nmos/control_protocol_resources.cpp | 2 +- Development/nmos/control_protocol_state.cpp | 2 +- Development/nmos/control_protocol_typedefs.h | 6 ++-- 5 files changed, 40 insertions(+), 40 deletions(-) diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 3d7518d9c..f4899661e 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -834,7 +834,7 @@ namespace nmos ); } - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager web::json::value make_nc_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value &user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { using web::json::value; @@ -844,6 +844,7 @@ namespace nmos return data; } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesholder web::json::value make_nc_bulk_properties_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders) { using web::json::value_of; @@ -855,7 +856,7 @@ namespace nmos ); } - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyholder web::json::value make_nc_property_holder(const nc_property_id& property_id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, const web::json::value& property_value) { using web::json::value; @@ -870,7 +871,7 @@ namespace nmos }, true); } - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiesholder web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_holders, const web::json::array& dependency_paths, const web::json::array& allowed_members_classes, bool is_rebuildable) { using web::json::value_of; @@ -886,7 +887,7 @@ namespace nmos } - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenotice web::json::value make_nc_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message) { using web::json::value; @@ -901,7 +902,7 @@ namespace nmos ); } - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiessetvalidation web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const web::json::value& status_message) { using web::json::value; @@ -1315,7 +1316,7 @@ namespace nmos // Device configuration classes // NcBulkPropertiesManager - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager web::json::value make_nc_bulk_properties_manager_properties() { using web::json::value; @@ -1434,7 +1435,7 @@ namespace nmos } // Device configuration feature set control classes - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager web::json::value make_nc_bulk_properties_manager_class() { using web::json::value; @@ -2192,7 +2193,7 @@ namespace nmos } // Device Configuration datatypes - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestoremode web::json::value make_nc_restore_mode_datatype() { using web::json::value; @@ -2203,7 +2204,7 @@ namespace nmos return details::make_nc_datatype_descriptor_enum(U("Restore mode enumeration"), U("NcRestoreMode"), items, value::null()); } - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyholder web::json::value make_nc_property_holder_datatype() { using web::json::value; @@ -2217,7 +2218,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(U("Property holder descriptor"), U("NcPropertyHolder"), fields, value::null()); } - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiesholder web::json::value make_nc_object_properties_holder_datatype() { using web::json::value; @@ -2231,7 +2232,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(U("Object properties holder descriptor"), U("NcObjectPropertiesHolder"), fields, value::null()); } - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesholder web::json::value make_nc_bulk_properties_holder_datatype() { using web::json::value; @@ -2242,7 +2243,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(U("Bulk properties holder descriptor"), U("NcBulkPropertiesHolder"), fields, value::null()); } - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestorevalidationstatus web::json::value make_nc_restore_validation_status_datatype() { using web::json::value; @@ -2254,7 +2255,7 @@ namespace nmos web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed due to an internal device error preventing the restore from happening"), U("DeviceError"), 500)); return details::make_nc_datatype_descriptor_enum(U("Restore validation status enumeration"), U("NcRestoreValidationStatus"), items, value::null()); } - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenoticetype web::json::value make_nc_property_restore_notice_type_datatype() { using web::json::value; @@ -2264,7 +2265,7 @@ namespace nmos web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Error property restore notice"), U("Error"), nc_property_restore_notice_type::error)); return details::make_nc_datatype_descriptor_enum(U("Property restore notice type enumeration"), U("NcPropertyRestoreNoticeType"), items, value::null()); } - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenotice web::json::value make_nc_property_restore_notice_datatype() { using web::json::value; @@ -2277,7 +2278,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(U("Property restore notice descriptor"), U("NcPropertyRestoreNotice"), fields, value::null()); } - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiessetvalidation web::json::value make_nc_object_properties_set_validation_datatype() { using web::json::value; @@ -2290,7 +2291,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(U("Object properties set validation descriptor"), U("NcObjectPropertiesSetValidation"), fields, value::null()); } - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncmethodresultbulkpropertiesholder web::json::value make_nc_method_result_bulk_properties_holder_datatype() { using web::json::value; @@ -2300,7 +2301,7 @@ namespace nmos return details::make_nc_datatype_descriptor_struct(U("Method result containing bulk properties holder descriptor"), U("NcMethodResultBulkPropertiesHolder"), fields, U("NcMethodResult"), value::null()); } - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncmethodresultobjectpropertiessetvalidation web::json::value make_nc_method_result_object_properties_set_validation_datatype() { using web::json::value; diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 01b430420..281aa7827 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -192,22 +192,22 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state); - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager web::json::value make_nc_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesholder web::json::value make_nc_bulk_properties_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders); - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyholder web::json::value make_nc_property_holder(const nc_property_id& property_id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, const web::json::value& property_value); - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiesholder web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_holders, const web::json::array& dependency_paths, const web::json::array& allowed_members_classes, bool is_rebuildable); - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenotice web::json::value make_nc_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message); - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiessetvalidation web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const web::json::value& status_message); } @@ -300,7 +300,7 @@ namespace nmos web::json::value make_nc_ident_beacon_events(); // Device configuration classes - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager web::json::value make_nc_bulk_properties_manager_properties(); web::json::value make_nc_bulk_properties_manager_methods(); web::json::value make_nc_bulk_properties_manager_events(); @@ -454,26 +454,25 @@ namespace nmos web::json::value make_nc_payload_status_datatype(); // Device configuration feature set datatypes - // TODO: add link - // + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestoremode web::json::value make_nc_restore_mode_datatype(); - // + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyholder web::json::value make_nc_property_holder_datatype(); - // + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiesholder web::json::value make_nc_object_properties_holder_datatype(); - // + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesholder web::json::value make_nc_bulk_properties_holder_datatype(); - // + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestorevalidationstatus web::json::value make_nc_restore_validation_status_datatype(); - // + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenoticetype web::json::value make_nc_property_restore_notice_type_datatype(); - // + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenotice web::json::value make_nc_property_restore_notice_datatype(); - // + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiessetvalidation web::json::value make_nc_object_properties_set_validation_datatype(); - // + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncmethodresultbulkpropertiesholder web::json::value make_nc_method_result_bulk_properties_holder_datatype(); - // + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncmethodresultobjectpropertiessetvalidation web::json::value make_nc_method_result_object_properties_set_validation_datatype(); } diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index 69c782a0f..080a4c96b 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -110,7 +110,7 @@ namespace nmos // Device Configuration feature set control classes // - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager control_protocol_resource make_bulk_properties_manager(nc_oid oid) { using web::json::value; diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index c1e756603..e13feb58f 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -468,7 +468,7 @@ namespace nmos { U("NcConnectionStatus"), {make_nc_connection_status_datatype()} }, { U("NcPayloadStatus"), {make_nc_payload_status_datatype()} }, // Device configuration feature set - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#datatypes { U("NcRestoreMode"), {make_nc_restore_mode_datatype()} }, { U("NcPropertyHolder"), {make_nc_property_holder_datatype()}}, { U("NcObjectPropertiesHolder"), {make_nc_object_properties_holder_datatype()}}, diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index e2d975750..52a5c07c3 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -146,7 +146,7 @@ namespace nmos }; } - // NcPropertyRestoreNoticeType + // NcPropertyRestoreNoticeType namespace nc_property_restore_notice_type { enum type @@ -204,7 +204,7 @@ namespace nmos const nc_method_id nc_class_manager_get_control_class_method_id(3, 1); const nc_method_id nc_class_manager_get_datatype_method_id(3, 2); // NcMethodsIds for NcBulkPropertiesManager - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesholder const nc_method_id nc_bulk_properties_manager_get_properties_by_path_method_id(3, 1); const nc_method_id nc_bulk_properties_manager_validate_set_properties_by_path_method_id(3, 2); const nc_method_id nc_bulk_properties_manager_set_properties_by_path_method_id(3, 3); @@ -307,7 +307,7 @@ namespace nmos const nc_class_id nc_receiver_monitor_class_id({ 1, 2, 3 }); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitorprotected const nc_class_id nc_receiver_monitor_protected_class_id({ 1, 2, 3, 1 }); - // TODO: add link + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager const nc_class_id nc_bulk_properties_manager_class_id({ 1, 3, 3 }); // NcTouchpoint From 682a15ef9c4f22f25e967b356fba717eafe6f79f Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 8 Jul 2025 17:41:16 +0100 Subject: [PATCH 195/250] Change to read lock as no resouces will be modified in validating bulk properties API --- Development/nmos/configuration_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 5647da53d..7d7e87f04 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -667,7 +667,7 @@ namespace nmos return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object, version, &gate_](value body) mutable { - auto lock = model.write_lock(); + auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) From 7b9ed45b9d289c71b0f46c12e379889a993c05f5 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Tue, 8 Jul 2025 17:08:33 +0100 Subject: [PATCH 196/250] Add allowed_member_classes and dependency_paths fields --- .../nmos-cpp-node/node_implementation.cpp | 7 ++-- Development/nmos/configuration_methods.cpp | 5 ++- .../nmos/control_protocol_resource.cpp | 8 ++++- .../nmos/control_protocol_resources.cpp | 34 +++++++++++++++++++ Development/nmos/control_protocol_resources.h | 6 ++++ Development/nmos/control_protocol_typedefs.h | 5 +++ 6 files changed, 61 insertions(+), 4 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 263725fc7..2e22fc65c 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1267,6 +1267,8 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr auto receiver_block = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receiver Monitors"), U("Receiver Monitors")); // making a block rebuildable allows block members to be added or removed by the Configuration API in Rebuild mode nmos::make_rebuildable(receiver_block); + // restrict the allowed classes for members of this block + nmos::set_block_allowed_member_classes(receiver_block, {nmos::nc_receiver_monitor_class_id}); // example receiver-monitor(s) { @@ -1280,8 +1282,9 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr utility::ostringstream_t role; role << U("monitor-") << ++count; const auto& receiver = nmos::find_resource(model.node_resources, receiver_id); - const auto receiver_monitor = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, receiver_id}) } })); - + auto receiver_monitor = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, receiver_id}) } })); + // optionally indicate dependencies within the device model + nmos::set_object_dependency_paths(receiver_monitor, {{U("root"), U("receivers")}}); // add receiver-monitor to root-block nmos::nc::push_back(receiver_block, receiver_monitor); } diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index c8f759364..3c2fa2cf0 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -46,7 +46,10 @@ namespace nmos const auto role_path = get_role_path(resources, resource); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_holders, value::array().as_array(), value::array().as_array(), nmos::fields::nc::is_rebuildable(resource.data)); + const auto& dependency_paths = nmos::fields::nc::dependency_paths(resource.data); + const auto& allowed_member_classes = nmos::fields::nc::allowed_members_classes(resource.data); + + auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_holders, dependency_paths, allowed_member_classes, nmos::fields::nc::is_rebuildable(resource.data)); web::json::push_back(object_properties_holders, object_properties_holder); diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index f4899661e..e740c0790 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -712,9 +712,15 @@ namespace nmos data[nmos::fields::nc::touchpoints] = touchpoints; data[nmos::fields::nc::runtime_property_constraints] = runtime_property_constraints; // level 2 runtime constraints. See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // IS-14 isRebuilable flag + // IS-14 metadata fields + // These fields are "invisible" as they are not part of the NcObject definition // use make_rebuildable function to declare an control protocl resource rebuildable data[nmos::fields::nc::is_rebuildable] = value::boolean(false); + // use allowed_member_classes to restrict the types of object that an NcBlock can contain + data[nmos::fields::nc::allowed_members_classes] = value::array(); + // use to indicate dependencies of an object in the device model + data[nmos::fields::nc::dependency_paths] = value::array(); + return data; } diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index 080a4c96b..cc1ff6672 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -36,6 +36,40 @@ namespace nmos return control_protocol_resource; } + control_protocol_resource set_block_allowed_member_classes(control_protocol_resource& control_protocol_resource, const std::vector& allowed_member_classes) + { + using web::json::value; + + auto allowed_member_classes_array = value::array(); + + for(const auto& class_id: allowed_member_classes) + { + web::json::push_back(allowed_member_classes_array, nmos::details::make_nc_class_id(class_id)); + } + + control_protocol_resource.data[nmos::fields::nc::allowed_members_classes] = allowed_member_classes_array; + + return control_protocol_resource; + } + + control_protocol_resource set_object_dependency_paths(control_protocol_resource& control_protocol_resource, const std::vector& dependency_paths) + { + using web::json::value; + + auto dependency_path_array = value::array(); + + for(const auto& path: dependency_paths) + { + auto role_path = value::array(); + for (const auto path_item : path) { web::json::push_back(role_path, path_item); } + web::json::push_back(dependency_path_array, role_path); + } + + control_protocol_resource.data[nmos::fields::nc::dependency_paths] = dependency_path_array; + + return control_protocol_resource; + } + // create Root block resource control_protocol_resource make_root_block() { diff --git a/Development/nmos/control_protocol_resources.h b/Development/nmos/control_protocol_resources.h index 25526cca9..ea4dc4877 100644 --- a/Development/nmos/control_protocol_resources.h +++ b/Development/nmos/control_protocol_resources.h @@ -19,6 +19,12 @@ namespace nmos // make object rebuildable - for IS-14 dynamic configuration of Device Model control_protocol_resource make_rebuildable(control_protocol_resource& control_protocol_resource); + // set the allowed_member_classes field of an NcBlock - for IS-14 dynamic configuration of Device Model + control_protocol_resource set_block_allowed_member_classes(control_protocol_resource& control_protocol_resource, const std::vector& allowed_member_classes); + + // set the dependency_paths field of an NcObject - for IS-14 configuration of Device Model + control_protocol_resource set_object_dependency_paths(control_protocol_resource& control_protocol_resource, const std::vector& dependency_paths); + // create Root block resource control_protocol_resource make_root_block(); diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index 52a5c07c3..cf1cbdd88 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -289,6 +289,11 @@ namespace nmos // NcClassId // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassid typedef std::vector nc_class_id; + + // NcRolePath + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncrolepath + typedef std::vector nc_role_path; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject const nc_class_id nc_object_class_id({ 1 }); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock From 4693bdc8803bfd15b2a90266c6cb4e6472028a87 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Wed, 9 Jul 2025 11:07:12 +0100 Subject: [PATCH 197/250] Add modify test initial commit --- Development/nmos/configuration_utils.cpp | 41 ++++++- Development/nmos/configuration_utils.h | 2 + .../nmos/test/configuration_utils_test.cpp | 116 ++++++++++++++++++ 3 files changed, 156 insertions(+), 3 deletions(-) diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 870df06e1..cb5cd0f44 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -13,8 +13,6 @@ namespace nmos { - typedef std::map object_properties_map; - namespace details { bool is_property_value_valid(web::json::value& property_restore_notices, const web::json::value& property_value, const web::json::value& property_descriptor, const web::json::value& restore_mode, bool is_rebuildable) @@ -145,6 +143,7 @@ namespace nmos const auto& reference_members = nmos::fields::nc::members(resource.data); const auto& block_oid = nmos::fields::nc::oid(resource.data); + const auto& allowed_member_classes = nmos::fields::nc::allowed_members_classes(resource.data); std::vector members_to_remove; std::vector members_to_add; @@ -291,7 +290,7 @@ namespace nmos { utility::stringstream_t ss; ss << U("Owner value in block property holder inconsistent with oid of Block. Block oid takes precidence for role=") << role; - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("owner"), nmos::nc_property_restore_notice_type::warning, ss.str()); web::json::push_back(added_object_notices, notice); } const auto& constant_oid_property_holder = nmos::get_property_holder(child_object_properties_holder->second, nmos::nc_object_constant_oid_property_id); @@ -305,6 +304,42 @@ namespace nmos web::json::push_back(block_notices, notice); } + // Validate that the class of the object to add is allowed + if (allowed_member_classes.size() > 0) + { + const auto& class_id_property_holder = nmos::get_property_holder(child_object_properties_holder->second, nmos::nc_object_class_id_property_id); + + if (class_id_property_holder != web::json::value::null()) + { + const auto& class_id = nmos::fields::nc::value(class_id_property_holder); + + const auto& filtered_classes = boost::copy_range>(allowed_member_classes + | boost::adaptors::filtered([&](const web::json::value& member) + { + return member.as_array() == class_id.as_array(); + }) + ); + + // If receiver monitor class not allowed then return with error + if (filtered_classes.size() == 0) + { + utility::stringstream_t ss; + ss << U("Device model error: attempting to add unexpected class for role=") << role; + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("class_id"), nmos::nc_property_restore_notice_type::error, ss.str()); + web::json::push_back(added_object_notices, notice); + continue; + } + } + else + { + utility::stringstream_t ss; + ss << U("Class ID property value holder missing for role=") << role; + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("class_id"), nmos::nc_property_restore_notice_type::error, ss.str()); + web::json::push_back(added_object_notices, notice); + continue; + } + } + const auto& user_label_property_holder = nmos::get_property_holder(child_object_properties_holder->second, nmos::nc_object_user_label_property_id); const auto& user_label = (user_label_property_holder == web::json::value::null()) ? block_member_user_label : nmos::fields::nc::value(user_label_property_holder).as_string(); diff --git a/Development/nmos/configuration_utils.h b/Development/nmos/configuration_utils.h index 81bbaaffa..ed3de36b0 100644 --- a/Development/nmos/configuration_utils.h +++ b/Development/nmos/configuration_utils.h @@ -7,6 +7,8 @@ namespace nmos { + typedef std::map object_properties_map; + struct control_protocol_resource; // Check to see if role_path is sub path of parent_role_path diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 1cd93f259..016a02959 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -1573,3 +1573,119 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) } } } + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testModifyRebuildableBlock) +{ + using web::json::value_of; + using web::json::value; + + nmos::resources resources; + nmos::experimental::control_protocol_state control_protocol_state; + nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor = nmos::make_get_control_protocol_class_descriptor_handler(control_protocol_state); + + // Create Device Model + // root + auto root_block = nmos::make_root_block(); + auto oid = nmos::root_block_oid; + // root, ClassManager + auto class_manager = nmos::make_class_manager(++oid, control_protocol_state); + auto receiver_block_oid = ++oid; + // root, receivers + auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); + nmos::make_rebuildable(receivers); + // root, receivers, mon1 + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + // make monitor1 rebuildable + nmos::make_rebuildable(monitor1); + + auto monitor_1_oid = oid; + auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + // root, receivers, mon2 + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor_2_oid = oid; + nmos::nc::push_back(receivers, monitor1); + // add example-control to root-block + nmos::nc::push_back(receivers, monitor2); + // add stereo-gain to root-block + nmos::nc::push_back(root_block, receivers); + // add class-manager to root-block + nmos::nc::push_back(root_block, class_manager); + insert_resource(resources, std::move(root_block)); + insert_resource(resources, std::move(class_manager)); + insert_resource(resources, std::move(receivers)); + insert_resource(resources, std::move(monitor1)); + insert_resource(resources, std::move(monitor2)); + + bool filter_property_holders_called = false; + bool remove_device_model_object_called = false; + bool add_device_model_object_called = false; + + // callback stubs + nmos::filter_property_holders_handler filter_property_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + { + filter_property_holders_called = true; + auto modifiable_property_holders = value::array(); + + for (const auto& property_value : property_values) + { + web::json::push_back(modifiable_property_holders, property_value); + } + return modifiable_property_holders.as_array(); + }; + + nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::nc_oid reference_oid, bool validate) + { + remove_device_model_object_called = true; + + return true; + }; + + nmos::add_device_model_object_handler add_device_model_object = [&](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate) + { + add_device_model_object_called = true; + const auto& role_path = nmos::fields::nc::path(object_properties_holder); + return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::ok, U("OK")); + }; + + auto monitor_3_oid = 999; + // Create Object Properties Holder for Block, with a Property Holder for the block members + auto object_properties_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers") }); + { + auto property_holders = value::array(); + auto members = value::array(); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + // Create Object Properties Holder for new monitor + const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + { + auto property_holders = value::array(); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); + { + auto property_holders = value::array(); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool recurse = true; + bool validate = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + + nmos::object_properties_map object_properties_holder_map; + + for (const auto& object_properties_holder: object_properties_holders.as_array()) + { + const auto& role_path = nmos::fields::nc::path(object_properties_holder); + object_properties_holder_map.insert({ role_path, object_properties_holder }); + } + + modify_rebuildable_block(resources, object_properties_holder_map, resource, const web::json::array& target_role_path, const web::json::value& block_object_properties_holder, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, nmos::filter_property_holders_handler filter_property_holders) +} From 10dcaafdcad68b532f52fcce3c8b3c9de23c674b Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Wed, 9 Jul 2025 17:17:39 +0100 Subject: [PATCH 198/250] add tests for modify_rebuildable_block --- Development/nmos/configuration_utils.cpp | 18 ++ Development/nmos/configuration_utils.h | 5 + .../nmos/test/configuration_utils_test.cpp | 203 +++++++++++++++--- 3 files changed, 192 insertions(+), 34 deletions(-) diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index cb5cd0f44..0b186aeeb 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -327,6 +327,15 @@ namespace nmos ss << U("Device model error: attempting to add unexpected class for role=") << role; const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("class_id"), nmos::nc_property_restore_notice_type::error, ss.str()); web::json::push_back(added_object_notices, notice); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, added_object_notices.as_array(), ss.str()); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + // also create error notice for the block + const auto block_notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, ss.str()); + web::json::push_back(block_notices, block_notice); + + // erase object from object_properties_holder_map so it isn't processed subsequently + object_properties_holder_map.erase(child_role_path.as_array()); + continue; } } @@ -336,6 +345,15 @@ namespace nmos ss << U("Class ID property value holder missing for role=") << role; const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("class_id"), nmos::nc_property_restore_notice_type::error, ss.str()); web::json::push_back(added_object_notices, notice); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, added_object_notices.as_array(), ss.str()); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + // also create error notice for the block + const auto block_notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, ss.str()); + web::json::push_back(block_notices, block_notice); + + // erase object from object_properties_holder_map so it isn't processed subsequently + object_properties_holder_map.erase(child_role_path.as_array()); + continue; } } diff --git a/Development/nmos/configuration_utils.h b/Development/nmos/configuration_utils.h index ed3de36b0..a6a117e09 100644 --- a/Development/nmos/configuration_utils.h +++ b/Development/nmos/configuration_utils.h @@ -11,6 +11,11 @@ namespace nmos struct control_protocol_resource; + namespace details + { + web::json::value modify_rebuildable_block(nmos::resources& resources, object_properties_map& object_properties_holder_map, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& block_object_properties_holder, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, nmos::filter_property_holders_handler filter_property_holders); + } + // Check to see if role_path is sub path of parent_role_path bool is_role_path_root(const web::json::array& role_path_, const web::json::array& parent_role_path); diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 016a02959..e97fb8931 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -1577,7 +1577,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) //////////////////////////////////////////////////////////////////////////////////////////// BST_TEST_CASE(testModifyRebuildableBlock) { - using web::json::value_of; + using web::json::value_of; using web::json::value; nmos::resources resources; @@ -1594,6 +1594,7 @@ BST_TEST_CASE(testModifyRebuildableBlock) // root, receivers auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); nmos::make_rebuildable(receivers); + nmos::set_block_allowed_member_classes(receivers, {nmos::nc_receiver_monitor_class_id}); // root, receivers, mon1 auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); // make monitor1 rebuildable @@ -1647,45 +1648,179 @@ BST_TEST_CASE(testModifyRebuildableBlock) const auto& role_path = nmos::fields::nc::path(object_properties_holder); return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::ok, U("OK")); }; - - auto monitor_3_oid = 999; - // Create Object Properties Holder for Block, with a Property Holder for the block members - auto object_properties_holders = value::array(); - const auto role_path = value_of({ U("root"), U("receivers") }); { - auto property_holders = value::array(); - auto members = value::array(); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + auto monitor_3_oid = 999; + // Create Object Properties Holder for Block, with a Property Holder for the block members + auto object_properties_holders = value::array(); + auto block_property_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers") }); + { + auto members = value::array(); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(block_property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + } + const auto block_object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + push_back(object_properties_holders, block_object_properties_holder); + // Create Object Properties Holder for new monitor + const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + { + auto property_holders = value::array(); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + auto monitor3_property_holders = value::array(); + const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); + { + //auto property_holders = value::array(); + push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool validate = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + + nmos::object_properties_map object_properties_holder_map; + + for (const auto& object_properties_holder: object_properties_holders.as_array()) + { + const auto& role_path = nmos::fields::nc::path(object_properties_holder); + object_properties_holder_map.insert({ role_path, object_properties_holder }); + } + + const auto resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + + // allowed member classes specified for block but no class_id property holder in the new monitor object properties holder + const auto object_set_validations = nmos::details::modify_rebuildable_block(resources, object_properties_holder_map, *resource, target_role_path.as_array(), block_object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object, filter_property_holders); + + BST_REQUIRE_EQUAL(object_set_validations.size(), 2); + { + const auto& object_properties_set_validation = object_set_validations.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); + } + { + // warnings perhaps? + const auto& object_properties_set_validation = object_set_validations.at(1); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); + } } - // Create Object Properties Holder for new monitor - const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + + // add class id to the property holders, but use a disallowed class id { - auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + auto monitor_3_oid = 999; + // Create Object Properties Holder for Block, with a Property Holder for the block members + auto object_properties_holders = value::array(); + auto block_property_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers") }); + { + auto members = value::array(); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(block_property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + } + const auto block_object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + push_back(object_properties_holders, block_object_properties_holder); + // Create Object Properties Holder for new monitor + const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + { + auto property_holders = value::array(); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + auto monitor3_property_holders = value::array(); + const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); + { + //auto property_holders = value::array(); + push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); + push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, U("classId"), U("NcClassId"), true, nmos::details::make_nc_class_id(nmos::nc_block_class_id))); // disallowed class id + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool validate = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + + nmos::object_properties_map object_properties_holder_map; + + for (const auto& object_properties_holder: object_properties_holders.as_array()) + { + const auto& role_path = nmos::fields::nc::path(object_properties_holder); + object_properties_holder_map.insert({ role_path, object_properties_holder }); + } + + const auto resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + // allowed member classes specified for block but class_id property holder has disallowed class_id + const auto object_set_validations = nmos::details::modify_rebuildable_block(resources, object_properties_holder_map, *resource, target_role_path.as_array(), block_object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object, filter_property_holders); + + BST_REQUIRE_EQUAL(object_set_validations.size(), 2); + { + const auto& object_properties_set_validation = object_set_validations.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); + } + { + // warnings perhaps? + const auto& object_properties_set_validation = object_set_validations.at(1); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); + } } - const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); + + // add class id to the property holders, and use an allowed class id { - auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); - } - const auto target_role_path = value_of({ U("root"), U("receivers") }); - bool recurse = true; - bool validate = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + auto monitor_3_oid = 999; + // Create Object Properties Holder for Block, with a Property Holder for the block members + auto object_properties_holders = value::array(); + auto block_property_holders = value::array(); + const auto role_path = value_of({ U("root"), U("receivers") }); + { + auto members = value::array(); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(block_property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + } + const auto block_object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + push_back(object_properties_holders, block_object_properties_holder); + // Create Object Properties Holder for new monitor + const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); + { + auto property_holders = value::array(); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + auto monitor3_property_holders = value::array(); + const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); + { + //auto property_holders = value::array(); + push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); + push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, U("classId"), U("NcClassId"), true, nmos::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); + push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + } + const auto target_role_path = value_of({ U("root"), U("receivers") }); + bool validate = true; + const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; - nmos::object_properties_map object_properties_holder_map; + nmos::object_properties_map object_properties_holder_map; - for (const auto& object_properties_holder: object_properties_holders.as_array()) - { - const auto& role_path = nmos::fields::nc::path(object_properties_holder); - object_properties_holder_map.insert({ role_path, object_properties_holder }); - } + for (const auto& object_properties_holder: object_properties_holders.as_array()) + { + const auto& role_path = nmos::fields::nc::path(object_properties_holder); + object_properties_holder_map.insert({ role_path, object_properties_holder }); + } + + const auto resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); + // allowed member classes specified for block but class_id property holder has disallowed class_id + const auto object_set_validations = nmos::details::modify_rebuildable_block(resources, object_properties_holder_map, *resource, target_role_path.as_array(), block_object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object, filter_property_holders); - modify_rebuildable_block(resources, object_properties_holder_map, resource, const web::json::array& target_role_path, const web::json::value& block_object_properties_holder, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, nmos::filter_property_holders_handler filter_property_holders) + BST_REQUIRE_EQUAL(object_set_validations.size(), 2); + { + const auto& object_properties_set_validation = object_set_validations.at(0); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + } + { + // warnings perhaps? + const auto& object_properties_set_validation = object_set_validations.at(1); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + } + } } From 9aaad9fd8d6650f78eaebbe7baf6f23d18748210 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 10 Jul 2025 17:10:03 +0100 Subject: [PATCH 199/250] Remove unnecessary copy --- Development/nmos/configuration_api.cpp | 2 +- Development/nmos/control_protocol_resources.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 7d7e87f04..d3766b49a 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -420,7 +420,7 @@ namespace nmos inherited_struct = nc::details::get_datatype_descriptor(value::string(parent_type), get_control_protocol_datatype_descriptor); - for (const auto field : nmos::fields::nc::fields(inherited_struct)) + for (const auto& field : nmos::fields::nc::fields(inherited_struct)) { web::json::push_back(datatype_descriptor[nmos::fields::nc::fields], field); } diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index cc1ff6672..04318c662 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -61,7 +61,7 @@ namespace nmos for(const auto& path: dependency_paths) { auto role_path = value::array(); - for (const auto path_item : path) { web::json::push_back(role_path, path_item); } + for (const auto& path_item : path) { web::json::push_back(role_path, path_item); } web::json::push_back(dependency_path_array, role_path); } From 53642f17fbed25b11f90614930b2b2c7560f3d00 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 10 Jul 2025 17:14:14 +0100 Subject: [PATCH 200/250] Remove unnessary filter_property_holders handler from nmos::details::modify_rebuildable_block --- Development/nmos/configuration_utils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 0b186aeeb..7c2241fad 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -130,7 +130,7 @@ namespace nmos return nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, property_restore_notices.as_array(), U("OK")); } - web::json::value modify_rebuildable_block(nmos::resources& resources, object_properties_map& object_properties_holder_map, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& block_object_properties_holder, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, nmos::filter_property_holders_handler filter_property_holders) + web::json::value modify_rebuildable_block(nmos::resources& resources, object_properties_map& object_properties_holder_map, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& block_object_properties_holder, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) { auto object_properties_set_validations = web::json::value::array(); @@ -632,7 +632,7 @@ namespace nmos { // Process this block to add / remove device model objects as members of this block // the object properties holder for any added objects will be erased from the object_properties_holder_map to avoid double processing - const auto& child_object_properties_set_validations = details::modify_rebuildable_block(resources, object_properties_holder_map, *r, role_path, object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object, filter_property_holders); + const auto& child_object_properties_set_validations = details::modify_rebuildable_block(resources, object_properties_holder_map, *r, role_path, object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object); for (const auto& validation_values : child_object_properties_set_validations.as_array()) { web::json::push_back(object_properties_set_validation_values, validation_values); From 6f8cea2e0696dd4b2a5b89eec686cb815ec81c6a Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 10 Jul 2025 17:15:00 +0100 Subject: [PATCH 201/250] Tidy up --- Development/nmos/configuration_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 7c2241fad..52da0dbda 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -576,7 +576,7 @@ namespace nmos { const auto& role_path = nmos::fields::nc::path(object_properties_holder); // Only process role paths within the restore scope, or target_role_path only - if ((recurse && is_role_path_root(target_role_path, nmos::fields::nc::path(object_properties_holder))) || (!recurse && target_role_path == role_path)) + if ((recurse && is_role_path_root(target_role_path, role_path)) || (!recurse && target_role_path == role_path)) { const auto& find_role_paths = get_object_properties_holder(object_properties_holders, role_path); From ef62cf397ab6a86b8c99c2a6b8cd1a5b314bac81 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 10 Jul 2025 17:23:52 +0100 Subject: [PATCH 202/250] Update comment --- Development/nmos/node_server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index 3c53de8fc..3a00499f9 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -22,7 +22,7 @@ namespace nmos { namespace experimental { - // Construct a server instance for an NMOS Node, implementing the IS-04 Node API, IS-05 Connection API, IS-07 Events API, the IS-10 Authorization API + // Construct a server instance for an NMOS Node, implementing the IS-04 Node API, IS-05 Connection API, IS-07 Events API, IS-08 Channel Mapping API, IS-10 Authorization API, IS-12 Control Protocol API, IS-14 Configuration API, // and the experimental Logging API and Settings API, according to the specified data models and callbacks nmos::server make_node_server(nmos::node_model& node_model, nmos::experimental::node_implementation node_implementation, nmos::experimental::log_model& log_model, slog::base_gate& gate) { From 24028eb30e7ccaa27fdf8fa2225611c097dbda9a Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 11 Jul 2025 09:29:20 +0100 Subject: [PATCH 203/250] Fix test using the updated modify_rebuildable_block function signature --- Development/nmos/configuration_utils.h | 2 +- Development/nmos/test/configuration_utils_test.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Development/nmos/configuration_utils.h b/Development/nmos/configuration_utils.h index a6a117e09..89f4e4355 100644 --- a/Development/nmos/configuration_utils.h +++ b/Development/nmos/configuration_utils.h @@ -13,7 +13,7 @@ namespace nmos namespace details { - web::json::value modify_rebuildable_block(nmos::resources& resources, object_properties_map& object_properties_holder_map, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& block_object_properties_holder, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, nmos::filter_property_holders_handler filter_property_holders); + web::json::value modify_rebuildable_block(nmos::resources& resources, object_properties_map& object_properties_holder_map, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& block_object_properties_holder, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); } // Check to see if role_path is sub path of parent_role_path diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index e97fb8931..93070f142 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -1692,7 +1692,7 @@ BST_TEST_CASE(testModifyRebuildableBlock) const auto resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); // allowed member classes specified for block but no class_id property holder in the new monitor object properties holder - const auto object_set_validations = nmos::details::modify_rebuildable_block(resources, object_properties_holder_map, *resource, target_role_path.as_array(), block_object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object, filter_property_holders); + const auto object_set_validations = nmos::details::modify_rebuildable_block(resources, object_properties_holder_map, *resource, target_role_path.as_array(), block_object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object); BST_REQUIRE_EQUAL(object_set_validations.size(), 2); { @@ -1751,7 +1751,7 @@ BST_TEST_CASE(testModifyRebuildableBlock) const auto resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); // allowed member classes specified for block but class_id property holder has disallowed class_id - const auto object_set_validations = nmos::details::modify_rebuildable_block(resources, object_properties_holder_map, *resource, target_role_path.as_array(), block_object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object, filter_property_holders); + const auto object_set_validations = nmos::details::modify_rebuildable_block(resources, object_properties_holder_map, *resource, target_role_path.as_array(), block_object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object); BST_REQUIRE_EQUAL(object_set_validations.size(), 2); { @@ -1764,7 +1764,7 @@ BST_TEST_CASE(testModifyRebuildableBlock) BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); } } - + // add class id to the property holders, and use an allowed class id { auto monitor_3_oid = 999; @@ -1810,7 +1810,7 @@ BST_TEST_CASE(testModifyRebuildableBlock) const auto resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); // allowed member classes specified for block but class_id property holder has disallowed class_id - const auto object_set_validations = nmos::details::modify_rebuildable_block(resources, object_properties_holder_map, *resource, target_role_path.as_array(), block_object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object, filter_property_holders); + const auto object_set_validations = nmos::details::modify_rebuildable_block(resources, object_properties_holder_map, *resource, target_role_path.as_array(), block_object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object); BST_REQUIRE_EQUAL(object_set_validations.size(), 2); { From 562cfdc1a2bd22ac67587efcc35445ed02c35ed3 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Tue, 15 Jul 2025 10:37:27 +0100 Subject: [PATCH 204/250] Remove class checking from user callback --- .../nmos-cpp-node/node_implementation.cpp | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 2e22fc65c..4bd8ad909 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1818,25 +1818,6 @@ nmos::add_device_model_object_handler make_add_device_model_object_handler(nmos: nmos::resources& control_protocol_resources = model.control_protocol_resources; const auto& role_path = nmos::fields::nc::path(object_properties_holder); - const auto& allowed_member_classes = nmos::fields::nc::allowed_members_classes(object_properties_holder); - - if (allowed_member_classes.size() > 0) - { - // If allowed member classes array populated, ensure that the receiver monitor class is present - const auto& filtered_classes = boost::copy_range>(allowed_member_classes - | boost::adaptors::filtered([&](const web::json::value& member) - { - return nmos::details::parse_nc_class_id(member.as_array()) == nmos::nc_receiver_monitor_class_id; - }) - ); - - // If receiver monitor class not allowed then return with error - if (filtered_classes.size() == 0) - { - auto status_message = U("Device model error: attempting to add unexpected class"); - return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::device_error, status_message); - } - } const auto& touchpoint_property_holder = nmos::get_property_holder(object_properties_holder, nmos::nc_object_touchpoints_property_id); if (touchpoint_property_holder == web::json::value::null()) { From ce8fea9acd181bb84459065a8a1c801d02f7256c Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Tue, 15 Jul 2025 12:00:08 +0100 Subject: [PATCH 205/250] Further simplify user code callbacks --- .../nmos-cpp-node/node_implementation.cpp | 57 ++++-------- Development/nmos/configuration_handlers.h | 2 +- Development/nmos/configuration_utils.cpp | 89 +++++++++++++++---- .../nmos/test/configuration_utils_test.cpp | 8 +- 4 files changed, 95 insertions(+), 61 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 4bd8ad909..d59be2ee6 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1743,19 +1743,22 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control }; } -// Example Device Configuration callback called when a rebuildable object is modified in Rebuild mode. -// An array of property values is passed in, and an array of property values that can be modified is returned -// For each property value that can't be returned a property restore notice must be created +// Example Device Configuration callbacks called when a rebuildable object is modified in Rebuild mode. + +// The filter_property_holders function is called when the Device Configuration API is attempting to modify +// the read only properties of a Device Model object. This callback returns an "allow list" of property ids +// for properties that can be updated - the read only property will be updated to the value given in the NcPropertyHolder +// For each "disallowed" property value that isn't returned (is filtered out) a property restore notice must be created nmos::filter_property_holders_handler make_filter_property_holders_handler(nmos::resources& resources, slog::base_gate& gate) { - return [&resources, &gate](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + return [&resources, &gate](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { - // Use this function to filter which of the properties in the object should be modified by the configuration API + // Use this function to create allow list of property ids for properties in the object should be modified by the configuration API slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do filter_property_holders"; nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); - auto modifiable_property_holders = web::json::value::array(); + auto modifiable_property_ids = web::json::value::array(); for (const auto& property_value : property_values) { @@ -1763,22 +1766,16 @@ nmos::filter_property_holders_handler make_filter_property_holders_handler(nmos: const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); // In this example we are not allowing "structural" parts of an object to be modified - if (nmos::fields::nc::name(property_descriptor) == nmos::fields::nc::oid.key - || nmos::fields::nc::name(property_descriptor) == nmos::fields::nc::constant_oid.key - || nmos::fields::nc::name(property_descriptor) == nmos::fields::nc::role.key - || nmos::fields::nc::name(property_descriptor) == nmos::fields::nc::class_id.key - || nmos::fields::nc::name(property_descriptor) == nmos::fields::nc::owner.key) - { - // We need to create a notice for any properties that will not be updated - 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("Update of read only properties not supported")); - web::json::push_back(property_restore_notices, property_restore_notice); - } - else + if (nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::oid.key + && nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::constant_oid.key + && nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::role.key + && nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::class_id.key + && nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::owner.key) { - web::json::push_back(modifiable_property_holders, property_value); + web::json::push_back(modifiable_property_ids, nmos::fields::nc::id(property_value)); } } - return modifiable_property_holders.as_array(); + return modifiable_property_ids.as_array(); }; } @@ -1786,24 +1783,8 @@ nmos::remove_device_model_object_handler make_remove_device_model_handler(nmos:: { return [&model, &gate](const nmos::nc_oid reference_oid, bool validate) { - nmos::resources& control_protocol_resources = model.control_protocol_resources; - - // get the receiver monitor resource - auto found = nmos::find_resource(control_protocol_resources, utility::conversions::details::to_string_t(reference_oid)); - - if (control_protocol_resources.end() != found) - { - if (validate) // If validate is true then delete the object, just indicate whether it's possible given the data supplied - { - return true; - } - auto erase_count = nmos::nc::erase_resource(control_protocol_resources, found->id); - if (erase_count > 0) - { - return true; - } - } - return false; + // Perform application code functions here + return true; }; } @@ -1838,7 +1819,7 @@ nmos::add_device_model_object_handler make_add_device_model_object_handler(nmos: auto receiver_monitor = nmos::make_receiver_monitor(oid, true, owner, role, user_label, U(""), web::json::value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})} })); nmos::nc::insert_resource(control_protocol_resources, std::move(receiver_monitor)); } - // Generate notices for any properties that have been unprocessed + // Define the properties that have been procesed, and generate notices for all other (unprocessed) properties in the object_properties_holder std::vector< nmos::nc_property_id > processed_properties = { nmos::nc_object_oid_property_id, nmos::nc_object_owner_property_id, nmos::nc_object_role_property_id, diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h index 7066ca496..fad8448c6 100644 --- a/Development/nmos/configuration_handlers.h +++ b/Development/nmos/configuration_handlers.h @@ -19,7 +19,7 @@ namespace nmos } // This callback is invoked if attempting to modify read only properties when restoring a configuration. // This function should modify the Device Model object directly and return a corresponding NcObjectPropertiesSetValidation object - typedef std::function filter_property_holders_handler; + typedef std::function filter_property_holders_handler; // This callback is invoked if attempting to modify a rebuildable block when restoring a configuration. // This function should handle the modification of the Device Model and any corresponding NMOS resources diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 52da0dbda..7f2d39f7f 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -94,19 +94,56 @@ namespace nmos ); auto property_modify_list = web::json::value_from_elements(filtered_property_values).as_array(); - if (details::is_contains_read_only_property(property_modify_list, class_id, get_control_protocol_class_descriptor)) + if (nmos::nc_restore_mode::rebuild == restore_mode.as_integer()) { - if (filter_property_holders) - { - // If the property_modify_list contains read only properties then we call back to the application code to - // check that it's OK to change those value. Bear in mind that these could be the class Id, or the oid or some other - // property that we don't want changed ordinarily - property_modify_list = filter_property_holders(resource, target_role_path, property_modify_list, true, validate, property_restore_notices.as_array(), get_control_protocol_class_descriptor); - } - else + // Find any read only properties + const auto& read_only_property_values = boost::copy_range>(filtered_property_values + | boost::adaptors::filtered([](const web::json::value& property_value) + { + return nmos::fields::nc::is_read_only(property_value); + }) + ); + if (read_only_property_values.size() > 0) // Call back to user code { - // Modify of read only properties not supported - return nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, property_restore_notices.as_array(), U("Modification of read only properties not supported")); + if (filter_property_holders) + { + // If the property_modify_list contains read only properties then we call back to the application code to + // check that it's OK to change those value. Bear in mind that these could be the class Id, or the oid or some other + // property that we don't want changed ordinarily + const auto& allow_list_read_only_property_ids = filter_property_holders(resource, target_role_path, web::json::value_from_elements(read_only_property_values).as_array(), get_control_protocol_class_descriptor); + + const auto& allowed_property_values = boost::copy_range>(filtered_property_values + | boost::adaptors::filtered([&property_restore_notices, allow_list_read_only_property_ids](const web::json::value& property_value) + { + // if it's read only and in the allow list then add it + if (!nmos::fields::nc::is_read_only(property_value)) + { + return true; + } + + for (const auto& allowed_property_id: allow_list_read_only_property_ids) + { + if (nmos::fields::nc::id(property_value) == allowed_property_id) + { + return true; + } + } + // Create a warning notice for any read only property not allowed by the allow list + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + 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("This read only property can not be modified.")); + web::json::push_back(property_restore_notices, property_restore_notice); + + return false; + }) + ); + + property_modify_list = web::json::value_from_elements(allowed_property_values).as_array(); + } + else + { + // Modify of read only properties not supported + return nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::failed, property_restore_notices.as_array(), U("Modification of read only properties not supported")); + } } } for (const auto& property_value : property_modify_list) @@ -166,20 +203,36 @@ namespace nmos if (filtered_members.size() != 1) { // can't find this role in restore dataset, so member has been removed - bool success = remove_device_model_object(nmos::fields::nc::oid(reference_member), validate); - if (success) + // get the receiver monitor resource + auto found = nmos::find_resource(resources, utility::conversions::details::to_string_t(nmos::fields::nc::oid(reference_member))); + + if (resources.end() != found) { - if (!validate) + if (!validate) // If validate is true then delete the object, just indicate whether it's possible given the data supplied { - members_to_remove.push_back(nmos::fields::nc::oid(reference_member)); + auto erase_count = nmos::nc::erase_resource(resources, found->id); + if (erase_count > 0) + { + members_to_remove.push_back(nmos::fields::nc::oid(reference_member)); + } + else + { + // unable to delete resource so report the error and don't update block + web::json::push_back(block_notices, nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Unable to delete resource in Device Model."))); + continue; + } + } + // callback to user code + if (!remove_device_model_object(nmos::fields::nc::oid(reference_member), validate)) + { + // error in user code + web::json::push_back(block_notices, nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Application error."))); } } else { // unable to delete resource so report the error and don't update block - auto notices = web::json::value::array(); - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Unable to delete resource from Device Model.")); - web::json::push_back(block_notices, notice); + web::json::push_back(block_notices, nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Unable to find resource in Device Model."))); } } } diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 93070f142..a2a27707b 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -382,7 +382,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool add_device_model_object_called = false; // callback stubs - nmos::filter_property_holders_handler filter_property_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::filter_property_holders_handler filter_property_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { filter_property_holders_called = true; auto modifiable_property_holders = value::array(); @@ -1084,7 +1084,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) bool add_device_model_object_called = false; // callback stubs - nmos::filter_property_holders_handler filter_property_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::filter_property_holders_handler filter_property_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { filter_property_holders_called = true; auto modifiable_property_holders = value::array(); @@ -1309,7 +1309,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) bool add_device_model_object_called = false; // callback stubs - nmos::filter_property_holders_handler filter_property_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::filter_property_holders_handler filter_property_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { filter_property_holders_called = true; auto modifiable_property_holders = value::array(); @@ -1623,7 +1623,7 @@ BST_TEST_CASE(testModifyRebuildableBlock) bool add_device_model_object_called = false; // callback stubs - nmos::filter_property_holders_handler filter_property_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, bool recurse, bool validate, web::json::array& property_restore_notices, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::filter_property_holders_handler filter_property_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { filter_property_holders_called = true; auto modifiable_property_holders = value::array(); From cb6dd098cbbe982f94f239a734e397bfa68b5edc Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Tue, 15 Jul 2025 14:28:57 +0100 Subject: [PATCH 206/250] Update comments --- Development/nmos-cpp-node/node_implementation.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index d59be2ee6..2007e33cc 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1781,9 +1781,11 @@ nmos::filter_property_holders_handler make_filter_property_holders_handler(nmos: nmos::remove_device_model_object_handler make_remove_device_model_handler(nmos::node_model& model, slog::base_gate& gate) { - return [&model, &gate](const nmos::nc_oid reference_oid, bool validate) + return [&model, &gate](const nmos::nc_oid oid, bool validate) { // Perform application code functions here + // oid - oid of Device Model resource beng deleted + // validate - true when only checks are performed, false when checks and deletion are performed return true; }; } From 646403b29190dc25efc8f3df32a1f33b15ae7519 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Tue, 15 Jul 2025 16:55:12 +0100 Subject: [PATCH 207/250] Update based on changes to NcPropertyHolder --- .../nmos-cpp-node/node_implementation.cpp | 5 +- Development/nmos/configuration_handlers.h | 2 +- Development/nmos/configuration_methods.cpp | 2 +- Development/nmos/configuration_utils.cpp | 38 +++-- .../nmos/control_protocol_resource.cpp | 11 +- Development/nmos/control_protocol_resource.h | 2 +- Development/nmos/json_fields.h | 2 + .../nmos/test/configuration_utils_test.cpp | 151 ++++++++++-------- 8 files changed, 117 insertions(+), 96 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 2007e33cc..db4fc30a3 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1792,7 +1792,7 @@ nmos::remove_device_model_object_handler make_remove_device_model_handler(nmos:: nmos::add_device_model_object_handler make_add_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate) { - return[&model, &gate](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate) + return[&model, &gate](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { // This example callback shows how to add a receiver monitor resource to the device model // The receivers block that contains the monitors must be rebuildable @@ -1831,7 +1831,8 @@ nmos::add_device_model_object_handler make_add_device_model_object_handler(nmos: for (const auto& property_holder: nmos::fields::nc::values(object_properties_holder)) { const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)); - const auto& name = nmos::fields::nc::name(property_holder).c_str(); + const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, nmos::nc_receiver_monitor_class_id, get_control_protocol_class_descriptor); + const auto& name = nmos::fields::nc::name(property_descriptor).c_str(); if (std::find(processed_properties.begin(), processed_properties.end(), property_id) == processed_properties.end()) { const auto notice = nmos::details::make_nc_property_restore_notice(property_id, name, nmos::nc_property_restore_notice_type::warning, U("Property unprocessed.")); diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h index fad8448c6..e99b69b18 100644 --- a/Development/nmos/configuration_handlers.h +++ b/Development/nmos/configuration_handlers.h @@ -34,7 +34,7 @@ namespace nmos // This callback is invoked if attempting to add a device model object to a rebuildable block when restoring a configuration. // This function should handle the modification of the Device Model and any corresponding NMOS resources // and return correpsonding NcObjectPropertiesSetValidation objects for the object added - typedef std::function add_device_model_object_handler; + typedef std::function add_device_model_object_handler; } #endif diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index 3c2fa2cf0..9f3c3a81e 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -28,7 +28,7 @@ namespace nmos for (const auto& property_descriptor : control_class_descriptor.property_descriptors.as_array()) { - value property_holder = nmos::details::make_nc_property_holder(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)), nmos::fields::nc::name(property_descriptor), nmos::fields::nc::type_name(property_descriptor), nmos::fields::nc::is_read_only(property_descriptor), resource.data.at(nmos::fields::nc::name(property_descriptor))); + value property_holder = nmos::details::make_nc_property_holder(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)), property_descriptor, resource.data.at(nmos::fields::nc::name(property_descriptor))); web::json::push_back(property_holders, property_holder); } diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 7f2d39f7f..a73d03984 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -18,22 +18,23 @@ namespace nmos bool is_property_value_valid(web::json::value& property_restore_notices, const web::json::value& property_value, const web::json::value& property_descriptor, const web::json::value& restore_mode, bool is_rebuildable) { const nmos::nc_property_id& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)); + const auto& property_value_descriptor = nmos::fields::nc::descriptor(property_value); bool is_valid = true; // Check the name of the property is correct - if (nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::name(property_value)) + if (nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::name(property_value_descriptor)) { utility::ostringstream_t os; - os << U("unexpected property name: expected ") << nmos::fields::nc::name(property_descriptor) << U(", actual ") << nmos::fields::nc::name(property_value); - 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::error, os.str()); + os << U("unexpected property name: expected ") << nmos::fields::nc::name(property_descriptor) << U(", actual ") << nmos::fields::nc::name(property_value_descriptor); + const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_value_descriptor), nmos::nc_property_restore_notice_type::error, os.str()); web::json::push_back(property_restore_notices, property_restore_notice); is_valid = false; } // Check the type of the property value is correct - if (nmos::fields::nc::type_name(property_descriptor) != nmos::fields::nc::type_name(property_value)) + if (nmos::fields::nc::type_name(property_descriptor) != nmos::fields::nc::type_name(property_value_descriptor)) { utility::ostringstream_t os; - os << U("unexpected property type: expected ") << nmos::fields::nc::type_name(property_descriptor) << U(", actual ") << nmos::fields::nc::type_name(property_value); - 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::error, os.str()); + os << U("unexpected property type: expected ") << nmos::fields::nc::type_name(property_descriptor) << U(", actual ") << nmos::fields::nc::type_name(property_value_descriptor); + const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_value_descriptor), nmos::nc_property_restore_notice_type::error, os.str()); web::json::push_back(property_restore_notices, property_restore_notice); is_valid = false; } @@ -41,7 +42,7 @@ namespace nmos if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && restore_mode != nmos::nc_restore_mode::restore_mode::rebuild) { - 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::error, U("read only properties can not be modified in Modify restore mode.")); + const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_value_descriptor), nmos::nc_property_restore_notice_type::error, U("read only properties can not be modified in Modify restore mode.")); web::json::push_back(property_restore_notices, property_restore_notice); is_valid = false; } @@ -49,7 +50,7 @@ namespace nmos if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && !is_rebuildable) { - 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::error, U("object must be rebuildable to allow modification of read only properties.")); + const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_value_descriptor), nmos::nc_property_restore_notice_type::error, U("object must be rebuildable to allow modification of read only properties.")); web::json::push_back(property_restore_notices, property_restore_notice); is_valid = false; } @@ -98,9 +99,12 @@ namespace nmos { // Find any read only properties const auto& read_only_property_values = boost::copy_range>(filtered_property_values - | boost::adaptors::filtered([](const web::json::value& property_value) + | boost::adaptors::filtered([class_id, get_control_protocol_class_descriptor](const web::json::value& property_value) { - return nmos::fields::nc::is_read_only(property_value); + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); + + return nmos::fields::nc::is_read_only(property_descriptor); }) ); if (read_only_property_values.size() > 0) // Call back to user code @@ -113,10 +117,12 @@ namespace nmos const auto& allow_list_read_only_property_ids = filter_property_holders(resource, target_role_path, web::json::value_from_elements(read_only_property_values).as_array(), get_control_protocol_class_descriptor); const auto& allowed_property_values = boost::copy_range>(filtered_property_values - | boost::adaptors::filtered([&property_restore_notices, allow_list_read_only_property_ids](const web::json::value& property_value) + | boost::adaptors::filtered([&property_restore_notices, get_control_protocol_class_descriptor, class_id, allow_list_read_only_property_ids](const web::json::value& property_value) { + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); // if it's read only and in the allow list then add it - if (!nmos::fields::nc::is_read_only(property_value)) + if (!nmos::fields::nc::is_read_only(property_descriptor)) { return true; } @@ -129,8 +135,7 @@ namespace nmos } } // Create a warning notice for any read only property not allowed by the allow list - const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); - 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("This read only property can not be modified.")); + const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::warning, U("This read only property can not be modified.")); web::json::push_back(property_restore_notices, property_restore_notice); return false; @@ -149,6 +154,7 @@ namespace nmos for (const auto& property_value : property_modify_list) { const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); // hmmm, ideally we would pass the value into modify_resource with the validate // flag, so that it's subject to property contraints and also the application code can decide if it's a legal value @@ -159,7 +165,7 @@ namespace nmos nc::modify_resource(resources, resource.id, [&](nmos::resource& resource_) { - resource_.data[nmos::fields::nc::name(property_value)] = value; + resource_.data[nmos::fields::nc::name(property_descriptor)] = value; }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { {property_id, nmos::nc_property_change_type::type::value_changed, value} })); } @@ -414,7 +420,7 @@ namespace nmos const auto& user_label_property_holder = nmos::get_property_holder(child_object_properties_holder->second, nmos::nc_object_user_label_property_id); const auto& user_label = (user_label_property_holder == web::json::value::null()) ? block_member_user_label : nmos::fields::nc::value(user_label_property_holder).as_string(); - auto object_properties_set_validation = add_device_model_object(child_object_properties_holder->second, oid, owner, role, user_label, validate); + auto object_properties_set_validation = add_device_model_object(child_object_properties_holder->second, oid, owner, role, user_label, validate, get_control_protocol_class_descriptor); // Add warnings about known inconsistancies between backup dataset and new device model object for (const auto& added_object_notice: added_object_notices.as_array()) { diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index e740c0790..ba1412ce3 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -863,16 +863,14 @@ namespace nmos } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyholder - web::json::value make_nc_property_holder(const nc_property_id& property_id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, const web::json::value& property_value) + web::json::value make_nc_property_holder(const nc_property_id& property_id, const web::json::value& descriptor, const web::json::value& property_value) { using web::json::value; using web::json::value_of; return value_of({ { nmos::fields::nc::id, make_nc_property_id(property_id)}, - { nmos::fields::nc::name, value::string(name)}, - { nmos::fields::nc::type_name, value::string(type_name)}, - { nmos::fields::nc::is_read_only, value::boolean(is_read_only)}, + { nmos::fields::nc::descriptor, descriptor}, { nmos::fields::nc::value, property_value}, }, true); } @@ -1338,6 +1336,7 @@ namespace nmos auto parameters = value::array(); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The target role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will return properties on specified path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true, property holders returned will contain non-null property descriptors and for full backups the ClassManager role path will also be included"), nmos::fields::nc::include_descriptors, U("NcBoolean"), false, false, value::null())); web::json::push_back(methods, details::make_nc_method_descriptor(U("Get bulk object properties by given path"), nc_bulk_properties_manager_get_properties_by_path_method_id, U("GetPropertiesByPath"), U("NcMethodResultBulkPropertiesHolder"), parameters, false)); } { @@ -2217,9 +2216,7 @@ namespace nmos auto fields = value::array(); web::json::push_back(fields, details::make_nc_field_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property type name. If null it means the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Is the property ReadOnly?"), nmos::fields::nc::is_read_only, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property descriptor"), nmos::fields::nc::descriptor, U("NcPropertyDescriptor"), true, false, value::null())); web::json::push_back(fields, details::make_nc_field_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); return details::make_nc_datatype_descriptor_struct(U("Property holder descriptor"), U("NcPropertyHolder"), fields, value::null()); diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 281aa7827..1a23efef6 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -199,7 +199,7 @@ namespace nmos web::json::value make_nc_bulk_properties_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyholder - web::json::value make_nc_property_holder(const nc_property_id& property_id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, const web::json::value& property_value); + web::json::value make_nc_property_holder(const nc_property_id& property_id, const web::json::value& descriptor, const web::json::value& property_value); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiesholder web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_holders, const web::json::array& dependency_paths, const web::json::array& allowed_members_classes, bool is_rebuildable); diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index 11de4eb6c..7fdb7f3b1 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -269,6 +269,7 @@ namespace nmos const web::json::field_as_array touchpoints{ U("touchpoints") }; const web::json::field_as_array runtime_property_constraints{ U("runtimePropertyConstraints") }; const web::json::field_as_bool recurse{ U("recurse") }; + const web::json::field_as_bool include_descriptors{ U("includeDescriptors") }; const web::json::field_as_bool enabled{ U("enabled") }; const web::json::field_as_array members{ U("members") }; const web::json::field_as_string description{ U("description") }; @@ -339,6 +340,7 @@ namespace nmos const web::json::field_as_array values{ U("values") }; const web::json::field_as_string validation_fingerprint{ U("validationFingerprint") }; const web::json::field_as_value status_message{ U("statusMessage") }; + const web::json::field_as_value descriptor{U("descriptor")}; const web::json::field_as_value data_set{ U("dataSet") }; // NcBulkPropertiesHolder const web::json::field_as_bool is_rebuildable{ U("isRebuildable") }; const web::json::field_as_integer notice_type{ U("noticeType") }; diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index a2a27707b..df4be938e 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -75,6 +75,8 @@ BST_TEST_CASE(testIsBlockModified) push_back(role_path, U("root")); push_back(role_path, U("receivers")); + const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + // Members unchanged { auto property_holders = value::array(); @@ -89,8 +91,7 @@ BST_TEST_CASE(testIsBlockModified) const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - const nmos::nc_property_id property_id(2, 2); // block members - const auto property_holder = nmos::details::make_nc_property_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); @@ -105,8 +106,7 @@ BST_TEST_CASE(testIsBlockModified) const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); const auto block_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, class_id, U("label"), receiver_block_oid); push_back(members, block_descriptor); - const nmos::nc_property_id property_id(2, 2); // block members - const auto property_holder = nmos::details::make_nc_property_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); @@ -127,8 +127,7 @@ BST_TEST_CASE(testIsBlockModified) const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), 20, true, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - const nmos::nc_property_id property_id(2, 2); // block members - const auto property_holder = nmos::details::make_nc_property_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); @@ -149,8 +148,7 @@ BST_TEST_CASE(testIsBlockModified) const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon4"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - const nmos::nc_property_id property_id(2, 2); // block members - const auto property_holder = nmos::details::make_nc_property_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); @@ -171,8 +169,7 @@ BST_TEST_CASE(testIsBlockModified) const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - const nmos::nc_property_id property_id(2, 2); // block members - const auto property_holder = nmos::details::make_nc_property_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); @@ -193,8 +190,7 @@ BST_TEST_CASE(testIsBlockModified) const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), 20); push_back(members, block_member_descriptor); } - const nmos::nc_property_id property_id(2, 2); // block members - const auto property_holder = nmos::details::make_nc_property_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); @@ -215,8 +211,7 @@ BST_TEST_CASE(testIsBlockModified) const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, false, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - const nmos::nc_property_id property_id(2, 2); // block members - const auto property_holder = nmos::details::make_nc_property_holder(property_id, U("members"), U("NcBlockMemberDescriptor"), false, members); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); @@ -230,13 +225,15 @@ BST_TEST_CASE(testGetObjectPropertiesHolder) using web::json::value_of; using web::json::value; + const auto enabled_property_descriptor = nmos::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, U("enabled"), U("NcBoolean"), false, false, false, false, web::json::value::null()); + // Create Object Properties Holder auto object_properties_holders = value::array(); { const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); @@ -244,7 +241,7 @@ BST_TEST_CASE(testGetObjectPropertiesHolder) { const auto role_path = value_of({ U("root"), U("receivers"), U("mon2") }); auto property_holders = value::array(); - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); @@ -252,7 +249,7 @@ BST_TEST_CASE(testGetObjectPropertiesHolder) { const auto role_path = value_of({ U("root"), U("senders"), U("mon1") }); auto property_holders = value::array(); - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); @@ -401,13 +398,14 @@ BST_TEST_CASE(testApplyBackupDataSet) return true; }; - nmos::add_device_model_object_handler add_device_model_object = [&](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate) + nmos::add_device_model_object_handler add_device_model_object = [&](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { add_device_model_object_called = true; const auto& role_path = nmos::fields::nc::path(object_properties_holder); return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::ok, U("OK")); }; + const auto enabled_property_descriptor = nmos::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, U("enabled"), U("NcBoolean"), false, false, false, false, web::json::value::null()); { // Check the successful modification of the "enabled" flag of mon1's worker base class in Modify mode // @@ -415,7 +413,7 @@ BST_TEST_CASE(testApplyBackupDataSet) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, value::boolean(false)); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); @@ -440,6 +438,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } + const auto connection_status_property_descriptor = nmos::details::make_nc_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, U("connectionStatusMessage"), U("NcString"), true, false, false, false, web::json::value::null()); { // Check filter_property_holders_handler is called when changing a read only property of rebuildable object in Rebuild mode // @@ -453,7 +452,7 @@ BST_TEST_CASE(testApplyBackupDataSet) auto property_holders = value::array(); const nmos::nc_property_id property_id(2, 1); // This is a read only property - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value("change this value")); push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); @@ -493,7 +492,7 @@ BST_TEST_CASE(testApplyBackupDataSet) auto property_holders = value::array(); const nmos::nc_property_id property_id(2, 1); // This is a read only property - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value("change this value")); push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); @@ -544,9 +543,9 @@ BST_TEST_CASE(testApplyBackupDataSet) auto property_holders = value::array(); const nmos::nc_property_id property_id(2, 1); // This is a read only property - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), true, value("change this value"))); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value("change this value"))); // This is a writable property - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, false)); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths @@ -581,6 +580,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } + const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); { // Check remove_device_model_object_called is called when trying to modify a rebuildable block // @@ -595,7 +595,7 @@ BST_TEST_CASE(testApplyBackupDataSet) auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -617,6 +617,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } + const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, U("oid"), U("NcOid"), true, false, false, false, web::json::value::null()); { // Check add_device_model_object_called is called when trying to modify a rebuildable block // @@ -634,20 +635,20 @@ BST_TEST_CASE(testApplyBackupDataSet) push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -702,7 +703,7 @@ BST_TEST_CASE(testApplyBackupDataSet) auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -732,8 +733,9 @@ BST_TEST_CASE(testApplyBackupDataSet) auto property_holders = value::array(); const nmos::nc_property_id property_id(2, 1); // This is a read only property - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value"))); //read only - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcString"), false, false)); // error in data type + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value("change this value"))); // read only + const auto property_descriptor = nmos::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, U("enabled"), U("NcString"), false, false, false, false, web::json::value::null()); // wrong data type + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, property_descriptor, false)); // error in data type push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -753,7 +755,7 @@ BST_TEST_CASE(testApplyBackupDataSet) // make sure the validation status propagates from the callback BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); - BST_REQUIRE_EQUAL(1, property_restore_notices.size()); + BST_REQUIRE_EQUAL(2, property_restore_notices.size()); const auto notice = *property_restore_notices.begin(); BST_CHECK_EQUAL(nmos::nc_property_id(2, 1), nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); @@ -779,7 +781,8 @@ BST_TEST_CASE(testApplyBackupDataSet) const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); // This is a read only property - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(3, 2), U("wrong_property_name"), U("NcString"), false, value("change this value"))); //read only + const auto property_descriptor = nmos::details::make_nc_property_descriptor(U("wrong_property_name"), nmos::nc_receiver_monitor_connection_status_message_property_id, U("wrong_property_name"), U("NcString"), false, false, false, false, web::json::value::null()); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, property_descriptor, value("change this value"))); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -825,7 +828,8 @@ BST_TEST_CASE(testApplyBackupDataSet) const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); // This is a read only property - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("wrong_data_type"), false, value("change this value"))); //read only + const auto property_descriptor = nmos::details::make_nc_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, U("connectionStatusMessage"), U("wrong_data_type"), false, false, false, false, web::json::value::null()); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, property_descriptor, value("change this value"))); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -907,6 +911,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) nmos::remove_device_model_object_handler remove_device_model_object; nmos::add_device_model_object_handler add_device_model_object; + const auto enabled_property_descriptor = nmos::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, U("enabled"), U("NcBoolean"), false, false, false, false, web::json::value::null()); { // Check that Modify mode is unaffected by undefined Rebuild mode callbacks // @@ -914,7 +919,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, false); push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); @@ -941,7 +946,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 1), U("enabled"), U("NcBoolean"), false, false); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, false); push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); @@ -970,7 +975,8 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto property_holders = value::array(); const nmos::nc_property_id property_id(2, 1); // This is a read only property - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_property_id(3, 2), U("connectionStatusMessage"), U("NcString"), false, value("change this value")); + const auto property_descriptor = nmos::details::make_nc_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, U("connectionStatusMessage"), U("NcString"), true, false, false, false, web::json::value::null()); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, property_descriptor, value("change this value")); push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); @@ -992,6 +998,8 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); } { + const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, U("oid"), U("NcOid"), true, false, false, false, web::json::value::null()); // Check undefined add_device_model_object and remove_device_model_object causes an unsupported error when attempting to modify a rebuildable block // // Create Object Properties Holder @@ -1001,12 +1009,12 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -1103,13 +1111,15 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) return true; }; - nmos::add_device_model_object_handler add_device_model_object = [&](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate) + nmos::add_device_model_object_handler add_device_model_object = [&](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { add_device_model_object_called = true; const auto& role_path = nmos::fields::nc::path(object_properties_holder); return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::ok, U("OK")); }; + const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, U("oid"), U("NcOid"), true, false, false, false, web::json::value::null()); { // Check new oid is generated for new device model object // @@ -1127,20 +1137,20 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, false, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -1208,20 +1218,20 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -1329,13 +1339,15 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) return false; }; - nmos::add_device_model_object_handler add_device_model_object = [&](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate) + nmos::add_device_model_object_handler add_device_model_object = [&](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { add_device_model_object_called = true; const auto& role_path = nmos::fields::nc::path(object_properties_holder); // Simulate error on adding object to device model return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::device_error, U("Unable to add object to device model")); }; + const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, U("oid"), U("NcOid"), true, false, false, false, web::json::value::null()); { // Check remove_device_model_object_called error is handled when attempting to modify a rebuildable block // @@ -1350,7 +1362,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -1391,12 +1403,12 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } @@ -1449,26 +1461,26 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -1532,16 +1544,16 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) auto members2 = value::array(); push_back(members1, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders1, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members1)); + push_back(property_holders1, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members1)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders1.as_array(), value::array().as_array(), value::array().as_array(), false)); // duplicate push_back(members2, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders2, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members2)); + push_back(property_holders2, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members2)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders2.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_1_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } @@ -1642,12 +1654,15 @@ BST_TEST_CASE(testModifyRebuildableBlock) return true; }; - nmos::add_device_model_object_handler add_device_model_object = [&](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate) + nmos::add_device_model_object_handler add_device_model_object = [&](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { add_device_model_object_called = true; const auto& role_path = nmos::fields::nc::path(object_properties_holder); return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::ok, U("OK")); }; + const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, U("oid"), U("NcOid"), true, false, false, false, web::json::value::null()); + const auto class_id_property_descriptor = nmos::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, U("classId"), U("NcClassId"), true, false, false, false, web::json::value::null()); { auto monitor_3_oid = 999; // Create Object Properties Holder for Block, with a Property Holder for the block members @@ -1659,7 +1674,7 @@ BST_TEST_CASE(testModifyRebuildableBlock) push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(block_property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(block_property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); } const auto block_object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, block_object_properties_holder); @@ -1667,14 +1682,14 @@ BST_TEST_CASE(testModifyRebuildableBlock) const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } auto monitor3_property_holders = value::array(); const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { //auto property_holders = value::array(); - push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); + push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -1718,7 +1733,7 @@ BST_TEST_CASE(testModifyRebuildableBlock) push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(block_property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(block_property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); } const auto block_object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, block_object_properties_holder); @@ -1726,15 +1741,15 @@ BST_TEST_CASE(testModifyRebuildableBlock) const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } auto monitor3_property_holders = value::array(); const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { //auto property_holders = value::array(); - push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); - push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, U("classId"), U("NcClassId"), true, nmos::details::make_nc_class_id(nmos::nc_block_class_id))); // disallowed class id + push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::details::make_nc_class_id(nmos::nc_block_class_id))); // disallowed class id push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -1777,7 +1792,7 @@ BST_TEST_CASE(testModifyRebuildableBlock) push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(block_property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(2, 2), U("members"), U("NcBlockMemberDescriptor"), true, members)); + push_back(block_property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); } const auto block_object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, block_object_properties_holder); @@ -1785,15 +1800,15 @@ BST_TEST_CASE(testModifyRebuildableBlock) const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_2_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } auto monitor3_property_holders = value::array(); const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { //auto property_holders = value::array(); - push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_property_id(1, 2), U("oid"), U("NcOid"), true, monitor_3_oid)); - push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, U("classId"), U("NcClassId"), true, nmos::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); + push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); From 714d0d7c2581a2e91e7e1fe208147e7f39895e90 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Wed, 16 Jul 2025 09:46:50 +0100 Subject: [PATCH 208/250] Implement includeDescriptors query parameter --- Development/nmos/configuration_api.cpp | 15 +++++++++++++- Development/nmos/configuration_methods.cpp | 20 ++++++++++++------- Development/nmos/configuration_methods.h | 2 +- Development/nmos/control_protocol_state.cpp | 3 ++- .../nmos/test/configuration_methods_test.cpp | 6 +++--- 5 files changed, 33 insertions(+), 13 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index d3766b49a..cc51b206a 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -146,6 +146,18 @@ namespace nmos return true; } + + bool parse_include_descriptors_query_parameter(const utility::string_t& query) + { + web::json::value arguments = web::json::value_from_query(query); + + if (arguments.has_field(fields::nc::include_descriptors)) + { + return U("false") != arguments.at(fields::nc::include_descriptors).as_string(); + } + + return true; + } } inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) @@ -629,8 +641,9 @@ namespace nmos try { bool recurse = details::parse_recurse_query_parameter(req.request_uri().query()); + bool include_descriptors = details::parse_include_descriptors_query_parameter(req.request_uri().query()); - method_result = get_properties_by_path(resources, *resource, recurse, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + method_result = get_properties_by_path(resources, *resource, recurse, include_descriptors, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); auto status = nmos::fields::nc::status(method_result); if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index 9f3c3a81e..ffdb5f7be 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -13,7 +13,7 @@ namespace nmos { namespace details { - web::json::array make_property_holders(const nmos::resource& resource, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + web::json::array make_property_holders(const nmos::resource& resource, bool include_descriptors, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { using web::json::value; @@ -28,7 +28,8 @@ namespace nmos for (const auto& property_descriptor : control_class_descriptor.property_descriptors.as_array()) { - value property_holder = nmos::details::make_nc_property_holder(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)), property_descriptor, resource.data.at(nmos::fields::nc::name(property_descriptor))); + const auto descriptor = include_descriptors ? property_descriptor : value::null(); + value property_holder = nmos::details::make_nc_property_holder(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)), descriptor, resource.data.at(nmos::fields::nc::name(property_descriptor))); web::json::push_back(property_holders, property_holder); } @@ -37,15 +38,20 @@ namespace nmos return property_holders.as_array(); } - void populate_object_property_holder(const nmos::resources& resources, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const nmos::resource& resource, bool recurse, web::json::value& object_properties_holders) + void populate_object_property_holder(const nmos::resources& resources, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, const nmos::resource& resource, bool recurse, bool include_descriptors, web::json::value& object_properties_holders) { using web::json::value; // Get property_holders for this resource - const auto& property_holders = make_property_holders(resource, get_control_protocol_class_descriptor); + const auto& property_holders = make_property_holders(resource, include_descriptors, get_control_protocol_class_descriptor); const auto role_path = get_role_path(resources, resource); + // when include_descriptors = false, don't include the Class Manager + if (!include_descriptors && role_path.size() > 1 && role_path.at(0).as_string() == U("root") && role_path.at(1).as_string() == U("ClassManager")) + { + return; + } const auto& dependency_paths = nmos::fields::nc::dependency_paths(resource.data); const auto& allowed_member_classes = nmos::fields::nc::allowed_members_classes(resource.data); @@ -66,7 +72,7 @@ namespace nmos if (resources.end() != found) { - populate_object_property_holder(resources, get_control_protocol_class_descriptor, *found, recurse, object_properties_holders); + populate_object_property_holder(resources, get_control_protocol_class_descriptor, *found, recurse, include_descriptors, object_properties_holders); } } } @@ -108,14 +114,14 @@ namespace nmos } } - web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) + web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, bool include_descriptors, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { using web::json::value; using web::json::value_of; value object_properties_holders = value::array(); - details::populate_object_property_holder(resources, get_control_protocol_class_descriptor, resource, recurse, object_properties_holders); + details::populate_object_property_holder(resources, get_control_protocol_class_descriptor, resource, recurse, include_descriptors, object_properties_holders); size_t validation_fingerprint = details::generate_validation_fingerprint(resources, resource); diff --git a/Development/nmos/configuration_methods.h b/Development/nmos/configuration_methods.h index cd50cb19b..545d9e060 100644 --- a/Development/nmos/configuration_methods.h +++ b/Development/nmos/configuration_methods.h @@ -15,7 +15,7 @@ namespace nmos struct control_protocol_resource; // Implementation of IS-14 function for creating backup dataset from a Device Model - web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor); + web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, bool include_descriptors, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor); web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index e13feb58f..9ce9e805a 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -185,8 +185,9 @@ namespace nmos return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); + bool include_descriptors = nmos::fields::nc::include_descriptors(arguments); - return nmos::get_properties_by_path(resources, resource, recurse, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + return nmos::get_properties_by_path(resources, resource, recurse, include_descriptors, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); }; } nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, filter_property_holders_handler filter_property_holders, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) diff --git a/Development/nmos/test/configuration_methods_test.cpp b/Development/nmos/test/configuration_methods_test.cpp index bfa8f1b42..594739a5f 100644 --- a/Development/nmos/test/configuration_methods_test.cpp +++ b/Development/nmos/test/configuration_methods_test.cpp @@ -57,7 +57,7 @@ BST_TEST_CASE(testGetPropertiesByPath) { const auto target_role_path = value_of({ U("root") }); const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - auto method_result = get_properties_by_path(resources, *resource, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + auto method_result = get_properties_by_path(resources, *resource, true, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); BST_REQUIRE_EQUAL(nmos::nc_method_status::ok, nmos::fields::nc::status(method_result)); @@ -69,7 +69,7 @@ BST_TEST_CASE(testGetPropertiesByPath) { const auto target_role_path = value_of({ U("root"), U("receivers") }); const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - auto method_result = get_properties_by_path(resources, *resource, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + auto method_result = get_properties_by_path(resources, *resource, true, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); BST_REQUIRE_EQUAL(nmos::nc_method_status::ok, nmos::fields::nc::status(method_result)); @@ -81,7 +81,7 @@ BST_TEST_CASE(testGetPropertiesByPath) { const auto target_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - auto method_result = get_properties_by_path(resources, *resource, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + auto method_result = get_properties_by_path(resources, *resource, true, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); BST_REQUIRE_EQUAL(nmos::nc_method_status::ok, nmos::fields::nc::status(method_result)); From bd456fa59538706bef0b539cdac32debc068eaad Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Wed, 16 Jul 2025 11:49:53 +0100 Subject: [PATCH 209/250] Simplify backup dataset validation --- Development/nmos/configuration_utils.cpp | 23 +-- .../nmos/test/configuration_utils_test.cpp | 144 ------------------ 2 files changed, 2 insertions(+), 165 deletions(-) diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index a73d03984..d48fb0f83 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -18,31 +18,12 @@ namespace nmos bool is_property_value_valid(web::json::value& property_restore_notices, const web::json::value& property_value, const web::json::value& property_descriptor, const web::json::value& restore_mode, bool is_rebuildable) { const nmos::nc_property_id& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)); - const auto& property_value_descriptor = nmos::fields::nc::descriptor(property_value); bool is_valid = true; - // Check the name of the property is correct - if (nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::name(property_value_descriptor)) - { - utility::ostringstream_t os; - os << U("unexpected property name: expected ") << nmos::fields::nc::name(property_descriptor) << U(", actual ") << nmos::fields::nc::name(property_value_descriptor); - const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_value_descriptor), nmos::nc_property_restore_notice_type::error, os.str()); - web::json::push_back(property_restore_notices, property_restore_notice); - is_valid = false; - } - // Check the type of the property value is correct - if (nmos::fields::nc::type_name(property_descriptor) != nmos::fields::nc::type_name(property_value_descriptor)) - { - utility::ostringstream_t os; - os << U("unexpected property type: expected ") << nmos::fields::nc::type_name(property_descriptor) << U(", actual ") << nmos::fields::nc::type_name(property_value_descriptor); - const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_value_descriptor), nmos::nc_property_restore_notice_type::error, os.str()); - web::json::push_back(property_restore_notices, property_restore_notice); - is_valid = false; - } // Only allow modification of read only properties when in Rebuild mode if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && restore_mode != nmos::nc_restore_mode::restore_mode::rebuild) { - const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_value_descriptor), nmos::nc_property_restore_notice_type::error, U("read only properties can not be modified in Modify restore mode.")); + const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::error, U("read only properties can not be modified in Modify restore mode.")); web::json::push_back(property_restore_notices, property_restore_notice); is_valid = false; } @@ -50,7 +31,7 @@ namespace nmos if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && !is_rebuildable) { - const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_value_descriptor), nmos::nc_property_restore_notice_type::error, U("object must be rebuildable to allow modification of read only properties.")); + const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::error, U("object must be rebuildable to allow modification of read only properties.")); web::json::push_back(property_restore_notices, property_restore_notice); is_valid = false; } diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index df4be938e..95fe36d08 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -720,150 +720,6 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } - { - // Mixture of filter_property_holders_handler and errors in Rebuild mode - // - filter_property_holders_called = false; - remove_device_model_object_called = false; - add_device_model_object_called = false; - - // Create Object Properties Holder - auto object_properties_holders = value::array(); - const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - auto property_holders = value::array(); - const nmos::nc_property_id property_id(2, 1); - // This is a read only property - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value("change this value"))); // read only - const auto property_descriptor = nmos::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, U("enabled"), U("NcString"), false, false, false, false, web::json::value::null()); // wrong data type - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, property_descriptor, false)); // error in data type - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); - // must be a more efficient way of initializing these role paths - const auto target_role_path = value_of({ U("root"), U("receivers") }); - bool recurse = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; - bool validate = true; - - const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); - - // expectation is there will be a result for each of the object_properties_holders i.e. one - BST_REQUIRE_EQUAL(1, output.as_array().size()); - - const auto object_properties_set_validation = output.as_array().at(0); - - const auto& property_restore_notices = nmos::fields::nc::notices(object_properties_set_validation); - // make sure the validation status propagates from the callback - BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); - - BST_REQUIRE_EQUAL(2, property_restore_notices.size()); - - const auto notice = *property_restore_notices.begin(); - BST_CHECK_EQUAL(nmos::nc_property_id(2, 1), nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); - BST_CHECK_EQUAL(U("enabled"), nmos::fields::nc::name(notice)); - BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); - BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); - - // expecting callback to filter_property_holders_called - // but not to modify_rebuildable_block_called - BST_CHECK(filter_property_holders_called); - BST_CHECK(!remove_device_model_object_called); - BST_CHECK(!add_device_model_object_called); - } - { - // Incorrect property name in property holders - // - filter_property_holders_called = false; - remove_device_model_object_called = false; - add_device_model_object_called = false; - - // Create Object Properties Holder - auto object_properties_holders = value::array(); - const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - auto property_holders = value::array(); - // This is a read only property - const auto property_descriptor = nmos::details::make_nc_property_descriptor(U("wrong_property_name"), nmos::nc_receiver_monitor_connection_status_message_property_id, U("wrong_property_name"), U("NcString"), false, false, false, false, web::json::value::null()); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, property_descriptor, value("change this value"))); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); - // must be a more efficient way of initializing these role paths - const auto target_role_path = value_of({ U("root"), U("receivers") }); - bool recurse = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; - bool validate = true; - - const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); - - // expectation is there will be a result for each of the object_properties_holders i.e. one - BST_REQUIRE_EQUAL(1, output.as_array().size()); - - const auto object_properties_set_validation = output.as_array().at(0); - - const auto& property_restore_notices = nmos::fields::nc::notices(object_properties_set_validation); - // make sure the validation status propagates from the callback - BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); - - BST_REQUIRE_EQUAL(1, property_restore_notices.size()); - - const auto notice = *property_restore_notices.begin(); - BST_CHECK_EQUAL(nmos::nc_property_id(3, 2), nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); - BST_CHECK_EQUAL(U("wrong_property_name"), nmos::fields::nc::name(notice)); - BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); - BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); - - // expecting callback to filter_property_holders_called - // but not to modify_rebuildable_block_called - BST_CHECK(!filter_property_holders_called); - BST_CHECK(!remove_device_model_object_called); - BST_CHECK(!add_device_model_object_called); - } - { - // Incorrect property type in property holders - // - filter_property_holders_called = false; - remove_device_model_object_called = false; - add_device_model_object_called = false; - - // Create Object Properties Holder - auto object_properties_holders = value::array(); - const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); - auto property_holders = value::array(); - // This is a read only property - const auto property_descriptor = nmos::details::make_nc_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, U("connectionStatusMessage"), U("wrong_data_type"), false, false, false, false, web::json::value::null()); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, property_descriptor, value("change this value"))); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); - // must be a more efficient way of initializing these role paths - const auto target_role_path = value_of({ U("root"), U("receivers") }); - bool recurse = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; - bool validate = true; - - const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); - - // expectation is there will be a result for each of the object_properties_holders i.e. one - BST_REQUIRE_EQUAL(1, output.as_array().size()); - - const auto object_properties_set_validation = output.as_array().at(0); - - const auto& property_restore_notices = nmos::fields::nc::notices(object_properties_set_validation); - // make sure the validation status propagates from the callback - BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); - - BST_REQUIRE_EQUAL(1, property_restore_notices.size()); - - const auto notice = *property_restore_notices.begin(); - BST_CHECK_EQUAL(nmos::nc_property_id(3, 2), nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); - BST_CHECK_EQUAL(U("connectionStatusMessage"), nmos::fields::nc::name(notice)); - BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); - BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); - - // expecting callback to filter_property_holders_called - // but not to modify_rebuildable_block_called - BST_CHECK(!filter_property_holders_called); - BST_CHECK(!remove_device_model_object_called); - BST_CHECK(!add_device_model_object_called); - } - // ensure an error if trying to invoke rebuildable block when in Modify mode } //////////////////////////////////////////////////////////////////////////////////////////// From aae3b6d6b187f78f2888d612b9b241ac9a9f3837 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Thu, 17 Jul 2025 17:06:21 +0100 Subject: [PATCH 210/250] Refactored filter_property_holders to get_read_only_modification_allow_list. --- Development/nmos-cpp-node/main.cpp | 2 +- .../nmos-cpp-node/node_implementation.cpp | 44 +++--- Development/nmos/configuration_api.cpp | 20 +-- Development/nmos/configuration_api.h | 2 +- Development/nmos/configuration_handlers.h | 2 +- Development/nmos/configuration_methods.cpp | 8 +- Development/nmos/configuration_methods.h | 4 +- Development/nmos/configuration_utils.cpp | 23 ++- Development/nmos/configuration_utils.h | 2 +- Development/nmos/control_protocol_state.cpp | 22 +-- Development/nmos/control_protocol_state.h | 2 +- Development/nmos/node_server.cpp | 2 +- Development/nmos/node_server.h | 8 +- .../nmos/test/configuration_utils_test.cpp | 146 ++++++++---------- 14 files changed, 137 insertions(+), 150 deletions(-) diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index 07c1d9011..11207d465 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -138,7 +138,7 @@ int main(int argc, char* argv[]) .on_request_authorization_code(nmos::experimental::make_request_authorization_code_handler(gate)); // may be omitted, only required for OAuth client which is using the Authorization Code Flow to obtain the access token } - nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.filter_property_holders, node_implementation.remove_device_model_object, node_implementation.add_device_model_object); + nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.get_read_only_modification_allow_list, node_implementation.remove_device_model_object, node_implementation.add_device_model_object); if (0 <= nmos::fields::control_protocol_ws_port(node_model.settings)) { node_implementation diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index db4fc30a3..67ed0fc97 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1745,40 +1745,40 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control // Example Device Configuration callbacks called when a rebuildable object is modified in Rebuild mode. -// The filter_property_holders function is called when the Device Configuration API is attempting to modify -// the read only properties of a Device Model object. This callback returns an "allow list" of property ids -// for properties that can be updated - the read only property will be updated to the value given in the NcPropertyHolder -// For each "disallowed" property value that isn't returned (is filtered out) a property restore notice must be created -nmos::filter_property_holders_handler make_filter_property_holders_handler(nmos::resources& resources, slog::base_gate& gate) +// This function is called when the Device Configuration API is attempting to modify +// the read only properties of a rebuildable Device Model object. This callback returns an "allow list" of property ids +// for properties that can be updated - the "allowed" read only property will be updated according to backup dataset received. +nmos::get_read_only_modification_allow_list_handler make_get_read_only_modification_allow_list_handler(nmos::resources& resources, slog::base_gate& gate) { - return [&resources, &gate](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + return [&resources, &gate](const nmos::resource& resource, const std::vector& target_role_path, const const std::vector& property_ids) { // Use this function to create allow list of property ids for properties in the object should be modified by the configuration API slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do filter_property_holders"; - nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + std::vector allow_list; - auto modifiable_property_ids = web::json::value::array(); - - for (const auto& property_value : property_values) + for (const auto& property_id : property_ids) { - const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); - const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); - - // In this example we are not allowing "structural" parts of an object to be modified - if (nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::oid.key - && nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::constant_oid.key - && nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::role.key - && nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::class_id.key - && nmos::fields::nc::name(property_descriptor) != nmos::fields::nc::owner.key) + if (property_id == nmos::nc_object_oid_property_id || property_id == nmos::nc_object_constant_oid_property_id + || property_id == nmos::nc_object_role_property_id || property_id == nmos::nc_object_class_id_property_id + || property_id == nmos::nc_object_owner_property_id) + { + // don't modify this property + } + else { - web::json::push_back(modifiable_property_ids, nmos::fields::nc::id(property_value)); + // allow modification of this read only property + allow_list.push_back(property_id); } } - return modifiable_property_ids.as_array(); + return allow_list; }; } +// This function is called before an object is deleted from the device model. +// If this function returns true and validate is false then the object will be deleted. +// If thus function returns false or validate is true then the object will not be deleted. +// If this function returns false an appropriate error will be passed to the calling client. nmos::remove_device_model_object_handler make_remove_device_model_handler(nmos::node_model& model, slog::base_gate& gate) { return [&model, &gate](const nmos::nc_oid oid, bool validate) @@ -1999,7 +1999,7 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode .on_validate_channelmapping_output_map(make_node_implementation_map_validator()) // may be omitted if not required .on_channelmapping_activated(make_node_implementation_channelmapping_activation_handler(gate)) .on_control_protocol_property_changed(make_node_implementation_control_protocol_property_changed_handler(gate)) // may be omitted if IS-12 not required - .on_filter_property_holders(make_filter_property_holders_handler(model.control_protocol_resources, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required + .on_get_read_only_modification_allow_list(make_get_read_only_modification_allow_list_handler(model.control_protocol_resources, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required .on_remove_device_model_object(make_remove_device_model_handler(model, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required .on_add_device_model_object(make_add_device_model_object_handler(model, gate)); // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required } diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index cc51b206a..31405dae6 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -18,9 +18,9 @@ namespace nmos { - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -51,7 +51,7 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object, property_changed, gate)); + configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object, property_changed, gate)); return configuration_api; } @@ -160,7 +160,7 @@ namespace nmos } } - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -673,12 +673,12 @@ namespace nmos }); // PATCH /rolePaths/{rolePath}/bulkProperties - invokes validation_set_properties_by_path method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object, version, &gate_](value body) mutable + return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object, version, &gate_](value body) mutable { auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; @@ -698,7 +698,7 @@ namespace nmos const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); const auto& backup_data_set = nmos::fields::nc::data_set(arguments); - method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); auto status = nmos::fields::nc::status(method_result); if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } @@ -737,12 +737,12 @@ namespace nmos }); // PUT /rolePaths/{rolePath}/bulkProperties - invokes set_properties_by_path method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object, version, &gate_](value body) mutable + return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object, version, &gate_](value body) mutable { auto lock = model.write_lock(); auto& resources = model.control_protocol_resources; @@ -762,7 +762,7 @@ namespace nmos const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); const auto& backup_data_set = nmos::fields::nc::data_set(arguments); - method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); code = status_codes::OK; diff --git a/Development/nmos/configuration_api.h b/Development/nmos/configuration_api.h index 0ce11749a..cb0c5c5c4 100644 --- a/Development/nmos/configuration_api.h +++ b/Development/nmos/configuration_api.h @@ -16,7 +16,7 @@ namespace nmos { struct node_model; - web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); } #endif diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h index e99b69b18..6fac47c58 100644 --- a/Development/nmos/configuration_handlers.h +++ b/Development/nmos/configuration_handlers.h @@ -19,7 +19,7 @@ namespace nmos } // This callback is invoked if attempting to modify read only properties when restoring a configuration. // This function should modify the Device Model object directly and return a corresponding NcObjectPropertiesSetValidation object - typedef std::function filter_property_holders_handler; + typedef std::function(const nmos::resource& resource, const std::vector& target_role_path, const const std::vector& property_ids)> get_read_only_modification_allow_list_handler; // This callback is invoked if attempting to modify a rebuildable block when restoring a configuration. // This function should handle the modification of the Device Model and any corresponding NMOS resources diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index ffdb5f7be..dc2fc8091 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -133,22 +133,22 @@ namespace nmos return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, bulk_properties_holder); } - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) { // Do something with validation fingerprint? const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } - web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) { // Do something with validation fingerprint? const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } diff --git a/Development/nmos/configuration_methods.h b/Development/nmos/configuration_methods.h index 545d9e060..da3fc1739 100644 --- a/Development/nmos/configuration_methods.h +++ b/Development/nmos/configuration_methods.h @@ -17,9 +17,9 @@ namespace nmos // Implementation of IS-14 function for creating backup dataset from a Device Model web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, bool include_descriptors, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor); - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); - web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); } #endif diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index d48fb0f83..29d6bd53c 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -54,7 +54,7 @@ namespace nmos return false; } - web::json::value modify_device_model_object(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& target_object_properties_holder, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_holders_handler filter_property_holders) + web::json::value modify_device_model_object(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& target_object_properties_holder, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list) { const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); @@ -90,12 +90,23 @@ namespace nmos ); if (read_only_property_values.size() > 0) // Call back to user code { - if (filter_property_holders) + if (get_read_only_modification_allow_list) { // If the property_modify_list contains read only properties then we call back to the application code to // check that it's OK to change those value. Bear in mind that these could be the class Id, or the oid or some other // property that we don't want changed ordinarily - const auto& allow_list_read_only_property_ids = filter_property_holders(resource, target_role_path, web::json::value_from_elements(read_only_property_values).as_array(), get_control_protocol_class_descriptor); + std::vector target_role_path_array; + for (const auto& element: target_role_path) + { + target_role_path_array.push_back(element.as_string()); + } + std::vector read_only_property_ids; + for (const auto& property_value: read_only_property_values) + { + read_only_property_ids.push_back(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value))); + } + + const auto& allow_list_read_only_property_ids = get_read_only_modification_allow_list(resource, target_role_path_array, read_only_property_ids); const auto& allowed_property_values = boost::copy_range>(filtered_property_values | boost::adaptors::filtered([&property_restore_notices, get_control_protocol_class_descriptor, class_id, allow_list_read_only_property_ids](const web::json::value& property_value) @@ -110,7 +121,7 @@ namespace nmos for (const auto& allowed_property_id: allow_list_read_only_property_ids) { - if (nmos::fields::nc::id(property_value) == allowed_property_id) + if (nmos::fields::nc::id(property_value) == nmos::details::make_nc_property_id(allowed_property_id)) { return true; } @@ -600,7 +611,7 @@ namespace nmos return web::json::value_from_elements(target_object_properties_holders).as_array(); } - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) { auto object_properties_set_validation_values = web::json::value::array(); @@ -688,7 +699,7 @@ namespace nmos } else { - const auto object_properties_set_validation = details::modify_device_model_object(resources, *r, role_path, object_properties_holder, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders); + const auto object_properties_set_validation = details::modify_device_model_object(resources, *r, role_path, object_properties_holder, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); } diff --git a/Development/nmos/configuration_utils.h b/Development/nmos/configuration_utils.h index 89f4e4355..987e4daaa 100644 --- a/Development/nmos/configuration_utils.h +++ b/Development/nmos/configuration_utils.h @@ -27,7 +27,7 @@ namespace nmos // Get object_properties_holder for specified target_role_path web::json::array get_object_properties_holder(const web::json::array& object_properties_holders, const web::json::array& target_role_path); - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::filter_property_holders_handler filter_property_holders, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); web::json::value get_property_holder(const web::json::value& object_properties_holder, const nmos::nc_property_id& property_id); } diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 9ce9e805a..ddea720c0 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -190,9 +190,9 @@ namespace nmos return nmos::get_properties_by_path(resources, resource, recurse, include_descriptors, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); }; } - nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, filter_property_holders_handler filter_property_holders, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) + nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) { - return [&get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [&get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -204,9 +204,9 @@ namespace nmos } auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); - if (filter_property_holders && remove_device_model_object && add_device_model_object) + if (get_read_only_modification_allow_list && remove_device_model_object && add_device_model_object) { - result = validate_set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + result = validate_set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -217,9 +217,9 @@ namespace nmos return result; }; } - nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, filter_property_holders_handler filter_property_holders, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) + nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) { - return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -231,9 +231,9 @@ namespace nmos } auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("callbacks not implemented")); - if (filter_property_holders && remove_device_model_object && add_device_model_object) + if (get_read_only_modification_allow_list && remove_device_model_object && add_device_model_object) { - result = set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + result = set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -246,7 +246,7 @@ namespace nmos } } - control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, filter_property_holders_handler filter_property_holders, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) + control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) { auto to_vector = [](const web::json::value& data) { @@ -385,8 +385,8 @@ namespace nmos to_methods_vector(make_nc_bulk_properties_manager_methods(), { { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this))}, - { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), filter_property_holders, remove_device_model_object, add_device_model_object) }, - { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), filter_property_holders, remove_device_model_object, add_device_model_object) } + { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object) }, + { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object) } }), to_vector(make_nc_bulk_properties_manager_events())) } }; diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index d8a75bded..6b80ca9a9 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -59,7 +59,7 @@ namespace nmos nmos::read_lock read_lock() const { return nmos::read_lock{ mutex }; } nmos::write_lock write_lock() const { return nmos::write_lock{ mutex }; } - control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, filter_property_holders_handler filter_property_holders = nullptr, remove_device_model_object_handler remove_device_model_object = nullptr, add_device_model_object_handler add_device_model_object = nullptr); + control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = nullptr, remove_device_model_object_handler remove_device_model_object = nullptr, add_device_model_object_handler add_device_model_object = nullptr); // insert control class descriptor, false if class descriptor already inserted bool insert(const experimental::control_class_descriptor& control_class_descriptor); // erase control class of the given class id, false if the required class not found diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index 3a00499f9..c5cc0155f 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -77,7 +77,7 @@ namespace nmos // Configure the Configuration API - node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.filter_property_holders, node_implementation.remove_device_model_object, node_implementation.add_device_model_object, node_implementation.control_protocol_property_changed, gate)); + node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.get_read_only_modification_allow_list, node_implementation.remove_device_model_object, node_implementation.add_device_model_object, node_implementation.control_protocol_property_changed, gate)); const auto& events_ws_port = nmos::fields::events_ws_port(node_model.settings); auto& events_ws_api = node_server.ws_handlers[{ {}, nmos::fields::events_ws_port(node_model.settings) }]; diff --git a/Development/nmos/node_server.h b/Development/nmos/node_server.h index 13faacdcd..c2d48e549 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -28,7 +28,7 @@ namespace nmos // underlying implementation into the server instance for the NMOS Node struct node_implementation { - node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::filter_property_holders_handler filter_property_holders, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) + node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) : load_server_certificates(std::move(load_server_certificates)) , load_dh_param(std::move(load_dh_param)) , load_ca_certificates(std::move(load_ca_certificates)) @@ -51,7 +51,7 @@ namespace nmos , get_control_protocol_datatype_descriptor(std::move(get_control_protocol_datatype_descriptor)) , get_control_protocol_method_descriptor(std::move(get_control_protocol_method_descriptor)) , control_protocol_property_changed(std::move(control_protocol_property_changed)) - , filter_property_holders(std::move(filter_property_holders)) + , get_read_only_modification_allow_list(std::move(get_read_only_modification_allow_list)) , remove_device_model_object(std::move(remove_device_model_object)) , add_device_model_object(std::move(add_device_model_object)) {} @@ -86,7 +86,7 @@ namespace nmos node_implementation& on_get_control_datatype_descriptor(nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { this->get_control_protocol_datatype_descriptor = std::move(get_control_protocol_datatype_descriptor); return *this; } node_implementation& on_get_control_protocol_method_descriptor(nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor) { this->get_control_protocol_method_descriptor = std::move(get_control_protocol_method_descriptor); return *this; } node_implementation& on_control_protocol_property_changed(nmos::control_protocol_property_changed_handler control_protocol_property_changed) { this->control_protocol_property_changed = std::move(control_protocol_property_changed); return *this; } - node_implementation& on_filter_property_holders(nmos::filter_property_holders_handler filter_property_holders) { this->filter_property_holders = std::move(filter_property_holders); return *this; } + node_implementation& on_get_read_only_modification_allow_list(nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list) { this->get_read_only_modification_allow_list = std::move(get_read_only_modification_allow_list); return *this; } node_implementation& on_remove_device_model_object(nmos::remove_device_model_object_handler remove_device_model_object) { this->remove_device_model_object = std::move(remove_device_model_object); return *this; } node_implementation& on_add_device_model_object(nmos::add_device_model_object_handler add_device_model_object) { this->add_device_model_object = std::move(add_device_model_object); return *this; } @@ -133,7 +133,7 @@ namespace nmos nmos::control_protocol_property_changed_handler control_protocol_property_changed; // Device Configuration handlers - nmos::filter_property_holders_handler filter_property_holders; + nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list; nmos::remove_device_model_object_handler remove_device_model_object; nmos::add_device_model_object_handler add_device_model_object; }; diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 95fe36d08..f82df7a9c 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -374,21 +374,15 @@ BST_TEST_CASE(testApplyBackupDataSet) insert_resource(resources, std::move(monitor1)); insert_resource(resources, std::move(monitor2)); - bool filter_property_holders_called = false; + bool get_read_only_modification_allow_list_called = false; bool remove_device_model_object_called = false; bool add_device_model_object_called = false; // callback stubs - nmos::filter_property_holders_handler filter_property_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resource& resource, const std::vector& target_role_path, const const std::vector& property_ids) { - filter_property_holders_called = true; - auto modifiable_property_holders = value::array(); - - for (const auto& property_value : property_values) - { - web::json::push_back(modifiable_property_holders, property_value); - } - return modifiable_property_holders.as_array(); + get_read_only_modification_allow_list_called = true; + return property_ids; }; nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::nc_oid reference_oid, bool validate) @@ -423,7 +417,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -434,15 +428,15 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); // not expecting callbacks to be invoked as no read only properties, or rebuildable blocks modified - BST_CHECK(!filter_property_holders_called); + BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } const auto connection_status_property_descriptor = nmos::details::make_nc_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, U("connectionStatusMessage"), U("NcString"), true, false, false, false, web::json::value::null()); { - // Check filter_property_holders_handler is called when changing a read only property of rebuildable object in Rebuild mode + // Check get_read_only_modification_allow_list_handler is called when changing a read only property of rebuildable object in Rebuild mode // - filter_property_holders_called = false; + get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; @@ -463,7 +457,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -473,16 +467,16 @@ BST_TEST_CASE(testApplyBackupDataSet) // make sure the validation status propagates from the callback BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); - // expecting callback to filter_property_holders_called + // expecting callback to get_read_only_modification_allow_list_called // but not to modify_rebuildable_block_called - BST_CHECK(filter_property_holders_called); + BST_CHECK(get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } { // Check error generated when attempting to change a read only property of non-rebuidable object in Rebuild mode // - filter_property_holders_called = false; + get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; @@ -503,7 +497,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -523,16 +517,16 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); - // expecting callback to filter_property_holders_called + // expecting callback to get_read_only_modification_allow_list_called // but not to modify_rebuildable_block_called - BST_CHECK(!filter_property_holders_called); + BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } { // Check an error is caused by trying to modify a read only property in Modify mode // - filter_property_holders_called = false; + get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; @@ -555,7 +549,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -576,7 +570,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); - BST_CHECK(!filter_property_holders_called); + BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } @@ -584,7 +578,7 @@ BST_TEST_CASE(testApplyBackupDataSet) { // Check remove_device_model_object_called is called when trying to modify a rebuildable block // - filter_property_holders_called = false; + get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; @@ -603,7 +597,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -613,7 +607,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); - BST_CHECK(!filter_property_holders_called); + BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } @@ -621,7 +615,7 @@ BST_TEST_CASE(testApplyBackupDataSet) { // Check add_device_model_object_called is called when trying to modify a rebuildable block // - filter_property_holders_called = false; + get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; @@ -657,7 +651,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors BST_REQUIRE_EQUAL(3, output.as_array().size()); @@ -685,14 +679,14 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); } - BST_CHECK(!filter_property_holders_called); + BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(add_device_model_object_called); } { // Check that role paths outside of the scope of the target role path are errored // - filter_property_holders_called = false; + get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; @@ -711,12 +705,12 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); // expectation no object_properties_holders as not in the restore scope BST_REQUIRE_EQUAL(0, output.as_array().size()); - BST_CHECK(!filter_property_holders_called); + BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } @@ -763,7 +757,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) insert_resource(resources, std::move(monitor2)); // undefined callback stubs - nmos::filter_property_holders_handler filter_property_holders; + nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list; nmos::remove_device_model_object_handler remove_device_model_object; nmos::add_device_model_object_handler add_device_model_object; @@ -785,7 +779,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -812,7 +806,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -823,7 +817,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); } { - // Check undefined filter_property_holders_handler causes an unsupported mode error when attempting to modify a read only property in Rebuild mode + // Check undefined get_read_only_modification_allow_list_handler causes an unsupported mode error when attempting to modify a read only property in Rebuild mode // // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -843,7 +837,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -879,7 +873,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holder BST_CHECK_EQUAL(2, output.as_array().size()); @@ -943,21 +937,15 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) insert_resource(resources, std::move(monitor1)); insert_resource(resources, std::move(monitor2)); - bool filter_property_holders_called = false; + bool get_read_only_modification_allow_list_called = false; bool remove_device_model_object_called = false; bool add_device_model_object_called = false; // callback stubs - nmos::filter_property_holders_handler filter_property_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resource& resource, const std::vector& target_role_path, const const std::vector& property_ids) { - filter_property_holders_called = true; - auto modifiable_property_holders = value::array(); - - for (const auto& property_value : property_values) - { - web::json::push_back(modifiable_property_holders, property_value); - } - return modifiable_property_holders.as_array(); + get_read_only_modification_allow_list_called = true; + return property_ids; }; nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::nc_oid reference_oid, bool validate) @@ -979,7 +967,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) { // Check new oid is generated for new device model object // - filter_property_holders_called = false; + get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; @@ -1015,7 +1003,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors BST_REQUIRE_EQUAL(3, output.as_array().size()); @@ -1054,14 +1042,14 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) BST_CHECK_EQUAL(nmos::fields::nc::index(property_id), nmos::nc_object_oid_property_id.index); } - BST_CHECK(!filter_property_holders_called); + BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(add_device_model_object_called); } { // Handle constant oid clash // - filter_property_holders_called = false; + get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; @@ -1096,7 +1084,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors BST_REQUIRE_EQUAL(3, output.as_array().size()); @@ -1121,7 +1109,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) BST_CHECK_EQUAL(nmos::nc_restore_validation_status::device_error, nmos::fields::nc::status(object_properties_set_validation)); } - BST_CHECK(!filter_property_holders_called); + BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } @@ -1170,21 +1158,15 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) insert_resource(resources, std::move(monitor1)); insert_resource(resources, std::move(monitor2)); - bool filter_property_holders_called = false; + bool get_read_only_modification_allow_list_called = false; bool remove_device_model_object_called = false; bool add_device_model_object_called = false; // callback stubs - nmos::filter_property_holders_handler filter_property_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resource& resource, const std::vector& target_role_path, const const std::vector& property_ids) { - filter_property_holders_called = true; - auto modifiable_property_holders = value::array(); - - for (const auto& property_value : property_values) - { - web::json::push_back(modifiable_property_holders, property_value); - } - return modifiable_property_holders.as_array(); + get_read_only_modification_allow_list_called = true; + return property_ids; }; nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::nc_oid reference_oid, bool validate) @@ -1207,7 +1189,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) { // Check remove_device_model_object_called error is handled when attempting to modify a rebuildable block // - filter_property_holders_called = false; + get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; @@ -1226,7 +1208,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -1241,14 +1223,14 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) BST_CHECK_EQUAL(nmos::fields::nc::level(property_id), nmos::nc_block_members_property_id.level); BST_CHECK_EQUAL(nmos::fields::nc::index(property_id), nmos::nc_block_members_property_id.index); - BST_CHECK(!filter_property_holders_called); + BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(remove_device_model_object_called); BST_CHECK(!add_device_model_object_called); } { // Check on remove_device_model_object_called error all other object properties holders are processed // - filter_property_holders_called = false; + get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; @@ -1274,7 +1256,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders BST_CHECK_EQUAL(2, output.as_array().size()); @@ -1303,7 +1285,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) { // Check add_device_model_object_called error is handled when trying to modify a rebuildable block // - filter_property_holders_called = false; + get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; @@ -1345,7 +1327,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors BST_CHECK_EQUAL(4, output.as_array().size()); @@ -1380,14 +1362,14 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); } - BST_CHECK(!filter_property_holders_called); + BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); BST_CHECK(add_device_model_object_called); } { // Check duplicate block object properties holders are handled // - filter_property_holders_called = false; + get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; add_device_model_object_called = false; @@ -1419,7 +1401,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, filter_property_holders, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); // expectation is there will be a result for each of the object_properties_holders BST_CHECK_EQUAL(3, output.as_array().size()); @@ -1486,21 +1468,15 @@ BST_TEST_CASE(testModifyRebuildableBlock) insert_resource(resources, std::move(monitor1)); insert_resource(resources, std::move(monitor2)); - bool filter_property_holders_called = false; + bool get_read_only_modification_allow_list_called = false; bool remove_device_model_object_called = false; bool add_device_model_object_called = false; // callback stubs - nmos::filter_property_holders_handler filter_property_holders = [&](const nmos::resource& resource, const web::json::array& target_role_path, const web::json::array& property_values, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resource& resource, const std::vector& target_role_path, const const std::vector& property_ids) { - filter_property_holders_called = true; - auto modifiable_property_holders = value::array(); - - for (const auto& property_value : property_values) - { - web::json::push_back(modifiable_property_holders, property_value); - } - return modifiable_property_holders.as_array(); + get_read_only_modification_allow_list_called = true; + return property_ids; }; nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::nc_oid reference_oid, bool validate) From a3f8e602a32cedba10ff6f67bc36c616da8ed7d6 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Thu, 17 Jul 2025 21:53:34 +0100 Subject: [PATCH 211/250] Refactored add_device_model_object to create_device_model_object --- Development/nmos-cpp-node/main.cpp | 2 +- .../nmos-cpp-node/node_implementation.cpp | 58 +---- Development/nmos/configuration_api.cpp | 20 +- Development/nmos/configuration_api.h | 2 +- Development/nmos/configuration_handlers.h | 12 +- Development/nmos/configuration_methods.cpp | 8 +- Development/nmos/configuration_methods.h | 4 +- Development/nmos/configuration_utils.cpp | 134 +++++++---- Development/nmos/configuration_utils.h | 4 +- Development/nmos/control_protocol_resource.h | 4 + Development/nmos/control_protocol_state.cpp | 22 +- Development/nmos/control_protocol_state.h | 2 +- Development/nmos/node_server.cpp | 2 +- Development/nmos/node_server.h | 8 +- .../nmos/test/configuration_utils_test.cpp | 215 ++++++++++-------- 15 files changed, 266 insertions(+), 231 deletions(-) diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index 11207d465..f269fb09e 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -138,7 +138,7 @@ int main(int argc, char* argv[]) .on_request_authorization_code(nmos::experimental::make_request_authorization_code_handler(gate)); // may be omitted, only required for OAuth client which is using the Authorization Code Flow to obtain the access token } - nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.get_read_only_modification_allow_list, node_implementation.remove_device_model_object, node_implementation.add_device_model_object); + nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.get_read_only_modification_allow_list, node_implementation.remove_device_model_object, node_implementation.create_device_model_object); if (0 <= nmos::fields::control_protocol_ws_port(node_model.settings)) { node_implementation diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 67ed0fc97..d8ed8d8af 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1750,7 +1750,7 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control // for properties that can be updated - the "allowed" read only property will be updated according to backup dataset received. nmos::get_read_only_modification_allow_list_handler make_get_read_only_modification_allow_list_handler(nmos::resources& resources, slog::base_gate& gate) { - return [&resources, &gate](const nmos::resource& resource, const std::vector& target_role_path, const const std::vector& property_ids) + return [&resources, &gate](const nmos::resource& resource, const std::vector& target_role_path, const std::vector& property_ids) { // Use this function to create allow list of property ids for properties in the object should be modified by the configuration API slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do filter_property_holders"; @@ -1779,7 +1779,7 @@ nmos::get_read_only_modification_allow_list_handler make_get_read_only_modificat // If this function returns true and validate is false then the object will be deleted. // If thus function returns false or validate is true then the object will not be deleted. // If this function returns false an appropriate error will be passed to the calling client. -nmos::remove_device_model_object_handler make_remove_device_model_handler(nmos::node_model& model, slog::base_gate& gate) +nmos::remove_device_model_object_handler make_remove_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate) { return [&model, &gate](const nmos::nc_oid oid, bool validate) { @@ -1790,57 +1790,23 @@ nmos::remove_device_model_object_handler make_remove_device_model_handler(nmos:: }; } -nmos::add_device_model_object_handler make_add_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate) +nmos::create_device_model_object_handler make_create_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate) { - return[&model, &gate](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + return[&model, &gate](const nmos::nc_class_id& class_id, nmos::nc_oid oid, bool constant_oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, bool validate, const std::map& property_values) { // This example callback shows how to add a receiver monitor resource to the device model // The receivers block that contains the monitors must be rebuildable - // To add a monitor, restore a backup dataset including an additional monitor in the members property of the receivers block, in Rebuild mode + // To add a monitor, restore a backup dataset including an additional monitor in the members property of the receivers block in Rebuild mode // Also include an object properties holder for the new monitor including a touchpoint property holder refencing the NMOS Receiver resource being monitored - nmos::resources& control_protocol_resources = model.control_protocol_resources; - - const auto& role_path = nmos::fields::nc::path(object_properties_holder); - const auto& touchpoint_property_holder = nmos::get_property_holder(object_properties_holder, nmos::nc_object_touchpoints_property_id); - if (touchpoint_property_holder == web::json::value::null()) - { - auto status_message = U("Cannot find touchpoint object property holder"); - return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::failed, status_message); - } - - const auto& touchpoints = nmos::fields::nc::value(touchpoint_property_holder); if (touchpoints.size() != 1) { - auto status_message = U("Either zero or more than one touchpoint found (ambiguous)."); - return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::failed, status_message); - } - if (!validate) // If validate is true then don't add object to device model, just indicate whether it's possible given the data supplied - { - const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); - - auto receiver_monitor = nmos::make_receiver_monitor(oid, true, owner, role, user_label, U(""), web::json::value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})} })); - nmos::nc::insert_resource(control_protocol_resources, std::move(receiver_monitor)); - } - // Define the properties that have been procesed, and generate notices for all other (unprocessed) properties in the object_properties_holder - std::vector< nmos::nc_property_id > processed_properties = { nmos::nc_object_oid_property_id, - nmos::nc_object_owner_property_id, - nmos::nc_object_role_property_id, - nmos::nc_object_user_label_property_id, - nmos::nc_object_touchpoints_property_id }; - auto notices = web::json::value::array(); - for (const auto& property_holder: nmos::fields::nc::values(object_properties_holder)) - { - const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)); - const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, nmos::nc_receiver_monitor_class_id, get_control_protocol_class_descriptor); - const auto& name = nmos::fields::nc::name(property_descriptor).c_str(); - if (std::find(processed_properties.begin(), processed_properties.end(), property_id) == processed_properties.end()) - { - const auto notice = nmos::details::make_nc_property_restore_notice(property_id, name, nmos::nc_property_restore_notice_type::warning, U("Property unprocessed.")); - web::json::push_back(notices, notice); - } + slog::log(gate, SLOG_FLF) << "Either zero or more than one touchpoint found (ambiguous) when attempting to create " << role; + return nmos::control_protocol_resource(); // return empty resource on error } + const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); - return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::ok, notices.as_array()); + // In the case of validate = true, the object created will not be added to the device model, but it's values will be checked against the backup dataset + return nmos::make_receiver_monitor(oid, true, owner, role, user_label, U(""), web::json::value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})} })); }; } @@ -2000,6 +1966,6 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode .on_channelmapping_activated(make_node_implementation_channelmapping_activation_handler(gate)) .on_control_protocol_property_changed(make_node_implementation_control_protocol_property_changed_handler(gate)) // may be omitted if IS-12 not required .on_get_read_only_modification_allow_list(make_get_read_only_modification_allow_list_handler(model.control_protocol_resources, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required - .on_remove_device_model_object(make_remove_device_model_handler(model, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required - .on_add_device_model_object(make_add_device_model_object_handler(model, gate)); // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required + .on_remove_device_model_object(make_remove_device_model_object_handler(model, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required + .on_create_device_model_object(make_create_device_model_object_handler(model, gate)); // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required } diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 31405dae6..9d3887371 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -18,9 +18,9 @@ namespace nmos { - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -51,7 +51,7 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object, property_changed, gate)); + configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object, property_changed, gate)); return configuration_api; } @@ -160,7 +160,7 @@ namespace nmos } } - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -673,12 +673,12 @@ namespace nmos }); // PATCH /rolePaths/{rolePath}/bulkProperties - invokes validation_set_properties_by_path method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object, version, &gate_](value body) mutable + return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object, version, &gate_](value body) mutable { auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; @@ -698,7 +698,7 @@ namespace nmos const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); const auto& backup_data_set = nmos::fields::nc::data_set(arguments); - method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); auto status = nmos::fields::nc::status(method_result); if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } @@ -737,12 +737,12 @@ namespace nmos }); // PUT /rolePaths/{rolePath}/bulkProperties - invokes set_properties_by_path method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object, version, &gate_](value body) mutable + return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object, version, &gate_](value body) mutable { auto lock = model.write_lock(); auto& resources = model.control_protocol_resources; @@ -762,7 +762,7 @@ namespace nmos const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); const auto& backup_data_set = nmos::fields::nc::data_set(arguments); - method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); code = status_codes::OK; diff --git a/Development/nmos/configuration_api.h b/Development/nmos/configuration_api.h index cb0c5c5c4..91e7f14b7 100644 --- a/Development/nmos/configuration_api.h +++ b/Development/nmos/configuration_api.h @@ -16,7 +16,7 @@ namespace nmos { struct node_model; - web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); } #endif diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h index 6fac47c58..0e52f237d 100644 --- a/Development/nmos/configuration_handlers.h +++ b/Development/nmos/configuration_handlers.h @@ -5,6 +5,7 @@ #include "nmos/control_protocol_typedefs.h" #include "nmos/control_protocol_handlers.h" #include "nmos/resources.h" +#include "nmos/control_protocol_resource.h" namespace slog { @@ -19,22 +20,17 @@ namespace nmos } // This callback is invoked if attempting to modify read only properties when restoring a configuration. // This function should modify the Device Model object directly and return a corresponding NcObjectPropertiesSetValidation object - typedef std::function(const nmos::resource& resource, const std::vector& target_role_path, const const std::vector& property_ids)> get_read_only_modification_allow_list_handler; - - // This callback is invoked if attempting to modify a rebuildable block when restoring a configuration. - // This function should handle the modification of the Device Model and any corresponding NMOS resources - // and return correpsonding NcObjectPropertiesSetValidation objects for each object modified/added - typedef std::function modify_rebuildable_block_handler; + typedef std::function(const nmos::resource& resource, const std::vector& target_role_path, const std::vector& property_ids)> get_read_only_modification_allow_list_handler; // This callback is invoked if attempting to remove a device model object when restoring a configuration. // This function should handle the modification of the Device Model and any corresponding NMOS resources // and return true if successful and false otherwise - typedef std::function remove_device_model_object_handler; + typedef std::function remove_device_model_object_handler; // This callback is invoked if attempting to add a device model object to a rebuildable block when restoring a configuration. // This function should handle the modification of the Device Model and any corresponding NMOS resources // and return correpsonding NcObjectPropertiesSetValidation objects for the object added - typedef std::function add_device_model_object_handler; + typedef std::function& property_values)> create_device_model_object_handler; } #endif diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index dc2fc8091..e537062a5 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -133,22 +133,22 @@ namespace nmos return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, bulk_properties_holder); } - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) { // Do something with validation fingerprint? const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } - web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) { // Do something with validation fingerprint? const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } diff --git a/Development/nmos/configuration_methods.h b/Development/nmos/configuration_methods.h index da3fc1739..cc0af336d 100644 --- a/Development/nmos/configuration_methods.h +++ b/Development/nmos/configuration_methods.h @@ -17,9 +17,9 @@ namespace nmos // Implementation of IS-14 function for creating backup dataset from a Device Model web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, bool include_descriptors, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor); - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); - web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); } #endif diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 29d6bd53c..de3052dd0 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -165,7 +165,7 @@ namespace nmos return nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, property_restore_notices.as_array(), U("OK")); } - web::json::value modify_rebuildable_block(nmos::resources& resources, object_properties_map& object_properties_holder_map, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& block_object_properties_holder, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) + web::json::value modify_rebuildable_block(nmos::resources& resources, object_properties_map& object_properties_holder_map, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& block_object_properties_holder, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) { auto object_properties_set_validations = web::json::value::array(); @@ -355,45 +355,43 @@ namespace nmos web::json::push_back(block_notices, notice); } - // Validate that the class of the object to add is allowed - if (allowed_member_classes.size() > 0) + const auto& class_id_property_holder = nmos::get_property_holder(child_object_properties_holder->second, nmos::nc_object_class_id_property_id); + + if (class_id_property_holder == web::json::value::null()) { - const auto& class_id_property_holder = nmos::get_property_holder(child_object_properties_holder->second, nmos::nc_object_class_id_property_id); + utility::stringstream_t ss; + ss << U("Class ID property value holder missing for role=") << role; + const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("class_id"), nmos::nc_property_restore_notice_type::error, ss.str()); + web::json::push_back(added_object_notices, notice); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, added_object_notices.as_array(), ss.str()); + web::json::push_back(object_properties_set_validations, object_properties_set_validation); + // also create error notice for the block + const auto block_notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, ss.str()); + web::json::push_back(block_notices, block_notice); - if (class_id_property_holder != web::json::value::null()) - { - const auto& class_id = nmos::fields::nc::value(class_id_property_holder); + // erase object from object_properties_holder_map so it isn't processed subsequently + object_properties_holder_map.erase(child_role_path.as_array()); - const auto& filtered_classes = boost::copy_range>(allowed_member_classes - | boost::adaptors::filtered([&](const web::json::value& member) - { - return member.as_array() == class_id.as_array(); - }) - ); + continue; + } - // If receiver monitor class not allowed then return with error - if (filtered_classes.size() == 0) - { - utility::stringstream_t ss; - ss << U("Device model error: attempting to add unexpected class for role=") << role; - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("class_id"), nmos::nc_property_restore_notice_type::error, ss.str()); - web::json::push_back(added_object_notices, notice); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, added_object_notices.as_array(), ss.str()); - web::json::push_back(object_properties_set_validations, object_properties_set_validation); - // also create error notice for the block - const auto block_notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, ss.str()); - web::json::push_back(block_notices, block_notice); + const auto& class_id = nmos::fields::nc::value(class_id_property_holder); - // erase object from object_properties_holder_map so it isn't processed subsequently - object_properties_holder_map.erase(child_role_path.as_array()); + // Validate that the class of the object to add is allowed + if (allowed_member_classes.size() > 0) + { + const auto& filtered_classes = boost::copy_range>(allowed_member_classes + | boost::adaptors::filtered([&](const web::json::value& member) + { + return member.as_array() == class_id.as_array(); + }) + ); - continue; - } - } - else + // If receiver monitor class not allowed then return with error + if (filtered_classes.size() == 0) { utility::stringstream_t ss; - ss << U("Class ID property value holder missing for role=") << role; + ss << U("Device model error: attempting to add unexpected class for role=") << role; const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("class_id"), nmos::nc_property_restore_notice_type::error, ss.str()); web::json::push_back(added_object_notices, notice); auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, added_object_notices.as_array(), ss.str()); @@ -412,21 +410,73 @@ namespace nmos const auto& user_label_property_holder = nmos::get_property_holder(child_object_properties_holder->second, nmos::nc_object_user_label_property_id); const auto& user_label = (user_label_property_holder == web::json::value::null()) ? block_member_user_label : nmos::fields::nc::value(user_label_property_holder).as_string(); - auto object_properties_set_validation = add_device_model_object(child_object_properties_holder->second, oid, owner, role, user_label, validate, get_control_protocol_class_descriptor); - // Add warnings about known inconsistancies between backup dataset and new device model object - for (const auto& added_object_notice: added_object_notices.as_array()) + const auto& touchpoints_property_holder = nmos::get_property_holder(child_object_properties_holder->second, nmos::nc_object_touchpoints_property_id); + const auto& touchpoints = (touchpoints_property_holder == web::json::value::null()) ? web::json::value::null() : nmos::fields::nc::value(touchpoints_property_holder); + + std::map property_values; + + for (const auto& property_holder: nmos::fields::nc::values(child_object_properties_holder->second)) { - web::json::push_back(nmos::fields::nc::notices(object_properties_set_validation), added_object_notice); + property_values.insert(std::pair(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)), nmos::fields::nc::value(property_holder))); } + auto parsed_class_id = nmos::details::parse_nc_class_id(class_id.as_array()); + + auto device_model_object = create_device_model_object(parsed_class_id, oid, constant_oid, owner, role, user_label, touchpoints, validate, property_values); - // If the status is anything other than OK assume the object wasn't created - if (nmos::fields::nc::status(object_properties_set_validation) == nmos::nc_restore_validation_status::ok && !validate) + if (device_model_object.has_data()) { + for (const auto& property_holder : nmos::fields::nc::values(child_object_properties_holder->second)) + { + const auto property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)); + const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, parsed_class_id, get_control_protocol_class_descriptor); + + if (device_model_object.data.has_field(nmos::fields::nc::name(property_descriptor))) + { + const auto& object_value = device_model_object.data[nmos::fields::nc::name(property_descriptor)]; + const auto& property_holder_value = nmos::fields::nc::value(property_holder); + //if (device_model_object.data[nmos::fields::nc::name(property_descriptor)] != nmos::fields::nc::value(property_holder)) + if (object_value != property_holder_value) + { + // warn + const auto notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::warning, U("Property not updated.")); + web::json::push_back(added_object_notices, notice); + } + } + else + { + // error doesn't have this property + const auto notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::warning, U("Property not member of created object.")); + web::json::push_back(added_object_notices, notice); + } + } + + // Add object to device model + nmos::nc::insert_resource(resources, std::move(device_model_object)); + auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(block_member_description, role, oid, constant_oid, nmos::nc_receiver_monitor_class_id, block_member_user_label, owner); members_to_add.push_back(block_member_descriptor); + web::json::push_back(object_properties_set_validations, nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok, added_object_notices.as_array())); } + else + { + // An error has occurred + web::json::push_back(object_properties_set_validations, nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::device_error, U("Unable to create Device Model object. This may be due to missing property values."))); + } + + + + //// Add warnings about known inconsistancies between backup dataset and new device model object + //for (const auto& added_object_notice: added_object_notices.as_array()) + //{ + // web::json::push_back(nmos::fields::nc::notices(object_properties_set_validation), added_object_notice); + //} + + //// If the status is anything other than OK assume the object wasn't created + //if (nmos::fields::nc::status(object_properties_set_validation) == nmos::nc_restore_validation_status::ok && !validate) + //{ + //} - web::json::push_back(object_properties_set_validations, object_properties_set_validation); + //web::json::push_back(object_properties_set_validations, object_properties_set_validation); // erase object from object_properties_holder_map so it isn't processed subsequently object_properties_holder_map.erase(child_role_path.as_array()); @@ -611,7 +661,7 @@ namespace nmos return web::json::value_from_elements(target_object_properties_holders).as_array(); } - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object) + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) { auto object_properties_set_validation_values = web::json::value::array(); @@ -679,11 +729,11 @@ namespace nmos if (nmos::nc::is_block(class_id) && nmos::fields::nc::is_rebuildable(r->data) && restore_mode == nmos::nc_restore_mode::rebuild && is_block_modified(*r, object_properties_holder)) { // Modify rebuildable block - if (remove_device_model_object && add_device_model_object) + if (remove_device_model_object && create_device_model_object) { // Process this block to add / remove device model objects as members of this block // the object properties holder for any added objects will be erased from the object_properties_holder_map to avoid double processing - const auto& child_object_properties_set_validations = details::modify_rebuildable_block(resources, object_properties_holder_map, *r, role_path, object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object); + const auto& child_object_properties_set_validations = details::modify_rebuildable_block(resources, object_properties_holder_map, *r, role_path, object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, create_device_model_object); for (const auto& validation_values : child_object_properties_set_validations.as_array()) { web::json::push_back(object_properties_set_validation_values, validation_values); diff --git a/Development/nmos/configuration_utils.h b/Development/nmos/configuration_utils.h index 987e4daaa..30ee24903 100644 --- a/Development/nmos/configuration_utils.h +++ b/Development/nmos/configuration_utils.h @@ -13,7 +13,7 @@ namespace nmos namespace details { - web::json::value modify_rebuildable_block(nmos::resources& resources, object_properties_map& object_properties_holder_map, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& block_object_properties_holder, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); + web::json::value modify_rebuildable_block(nmos::resources& resources, object_properties_map& object_properties_holder_map, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& block_object_properties_holder, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); } // Check to see if role_path is sub path of parent_role_path @@ -27,7 +27,7 @@ namespace nmos // Get object_properties_holder for specified target_role_path web::json::array get_object_properties_holder(const web::json::array& object_properties_holders, const web::json::array& target_role_path); - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::add_device_model_object_handler add_device_model_object); + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); web::json::value get_property_holder(const web::json::value& object_properties_holder, const nmos::nc_property_id& property_id); } diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 1a23efef6..6f0cbc7e5 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -18,6 +18,10 @@ namespace nmos { struct control_protocol_resource : resource { + control_protocol_resource() + : resource() + {} + control_protocol_resource(api_version version, nmos::type type, web::json::value&& data, nmos::id id, bool never_expire) : resource(version, type, std::move(data), id, never_expire) {} diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index ddea720c0..7b23600e8 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -190,9 +190,9 @@ namespace nmos return nmos::get_properties_by_path(resources, resource, recurse, include_descriptors, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); }; } - nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) + nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, create_device_model_object_handler create_device_model_object) { - return [&get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [&get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -204,9 +204,9 @@ namespace nmos } auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); - if (get_read_only_modification_allow_list && remove_device_model_object && add_device_model_object) + if (get_read_only_modification_allow_list && remove_device_model_object && create_device_model_object) { - result = validate_set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + result = validate_set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -217,9 +217,9 @@ namespace nmos return result; }; } - nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) + nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, create_device_model_object_handler create_device_model_object) { - return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -231,9 +231,9 @@ namespace nmos } auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("callbacks not implemented")); - if (get_read_only_modification_allow_list && remove_device_model_object && add_device_model_object) + if (get_read_only_modification_allow_list && remove_device_model_object && create_device_model_object) { - result = set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + result = set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -246,7 +246,7 @@ namespace nmos } } - control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) + control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, create_device_model_object_handler create_device_model_object) { auto to_vector = [](const web::json::value& data) { @@ -385,8 +385,8 @@ namespace nmos to_methods_vector(make_nc_bulk_properties_manager_methods(), { { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this))}, - { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object) }, - { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object) } + { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object) }, + { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object) } }), to_vector(make_nc_bulk_properties_manager_events())) } }; diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index 6b80ca9a9..183999e80 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -59,7 +59,7 @@ namespace nmos nmos::read_lock read_lock() const { return nmos::read_lock{ mutex }; } nmos::write_lock write_lock() const { return nmos::write_lock{ mutex }; } - control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = nullptr, remove_device_model_object_handler remove_device_model_object = nullptr, add_device_model_object_handler add_device_model_object = nullptr); + control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = nullptr, remove_device_model_object_handler remove_device_model_object = nullptr, create_device_model_object_handler create_device_model_object = nullptr); // insert control class descriptor, false if class descriptor already inserted bool insert(const experimental::control_class_descriptor& control_class_descriptor); // erase control class of the given class id, false if the required class not found diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index c5cc0155f..725da1b9a 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -77,7 +77,7 @@ namespace nmos // Configure the Configuration API - node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.get_read_only_modification_allow_list, node_implementation.remove_device_model_object, node_implementation.add_device_model_object, node_implementation.control_protocol_property_changed, gate)); + node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.get_read_only_modification_allow_list, node_implementation.remove_device_model_object, node_implementation.create_device_model_object, node_implementation.control_protocol_property_changed, gate)); const auto& events_ws_port = nmos::fields::events_ws_port(node_model.settings); auto& events_ws_api = node_server.ws_handlers[{ {}, nmos::fields::events_ws_port(node_model.settings) }]; diff --git a/Development/nmos/node_server.h b/Development/nmos/node_server.h index c2d48e549..2746c9d38 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -28,7 +28,7 @@ namespace nmos // underlying implementation into the server instance for the NMOS Node struct node_implementation { - node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, add_device_model_object_handler add_device_model_object) + node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, create_device_model_object_handler create_device_model_object) : load_server_certificates(std::move(load_server_certificates)) , load_dh_param(std::move(load_dh_param)) , load_ca_certificates(std::move(load_ca_certificates)) @@ -53,7 +53,7 @@ namespace nmos , control_protocol_property_changed(std::move(control_protocol_property_changed)) , get_read_only_modification_allow_list(std::move(get_read_only_modification_allow_list)) , remove_device_model_object(std::move(remove_device_model_object)) - , add_device_model_object(std::move(add_device_model_object)) + , create_device_model_object(std::move(create_device_model_object)) {} // use the default constructor and chaining member functions for fluent initialization @@ -88,7 +88,7 @@ namespace nmos node_implementation& on_control_protocol_property_changed(nmos::control_protocol_property_changed_handler control_protocol_property_changed) { this->control_protocol_property_changed = std::move(control_protocol_property_changed); return *this; } node_implementation& on_get_read_only_modification_allow_list(nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list) { this->get_read_only_modification_allow_list = std::move(get_read_only_modification_allow_list); return *this; } node_implementation& on_remove_device_model_object(nmos::remove_device_model_object_handler remove_device_model_object) { this->remove_device_model_object = std::move(remove_device_model_object); return *this; } - node_implementation& on_add_device_model_object(nmos::add_device_model_object_handler add_device_model_object) { this->add_device_model_object = std::move(add_device_model_object); return *this; } + node_implementation& on_create_device_model_object(nmos::create_device_model_object_handler create_device_model_object) { this->create_device_model_object = std::move(create_device_model_object); return *this; } // deprecated, use on_validate_connection_resource_patch node_implementation& on_validate_merged(nmos::details::connection_resource_patch_validator validate_merged) { return on_validate_connection_resource_patch(std::move(validate_merged)); } @@ -135,7 +135,7 @@ namespace nmos // Device Configuration handlers nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list; nmos::remove_device_model_object_handler remove_device_model_object; - nmos::add_device_model_object_handler add_device_model_object; + nmos::create_device_model_object_handler create_device_model_object; }; // Construct a server instance for an NMOS Node, implementing the IS-04 Node API, IS-05 Connection API, IS-07 Events API diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index f82df7a9c..ad8cb4d20 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -7,6 +7,7 @@ #include "nmos/configuration_handlers.h" #include "nmos/configuration_resources.h" #include "nmos/configuration_utils.h" +#include "nmos/is12_versions.h" #include "bst/test/test.h" @@ -376,10 +377,10 @@ BST_TEST_CASE(testApplyBackupDataSet) bool get_read_only_modification_allow_list_called = false; bool remove_device_model_object_called = false; - bool add_device_model_object_called = false; + bool create_device_model_object_called = false; // callback stubs - nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resource& resource, const std::vector& target_role_path, const const std::vector& property_ids) + nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resource& resource, const std::vector& target_role_path, const std::vector& property_ids) { get_read_only_modification_allow_list_called = true; return property_ids; @@ -392,14 +393,19 @@ BST_TEST_CASE(testApplyBackupDataSet) return true; }; - nmos::add_device_model_object_handler add_device_model_object = [&](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::create_device_model_object_handler create_device_model_object = [&](const nmos::nc_class_id& class_id, nmos::nc_oid oid, bool constant_oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, bool validate, const std::map& property_values) { - add_device_model_object_called = true; - const auto& role_path = nmos::fields::nc::path(object_properties_holder); - return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::ok, U("OK")); + using web::json::value; + + create_device_model_object_called = true; + + auto data = nmos::details::make_nc_object(nmos::nc_receiver_monitor_class_id, oid, true, owner, role, web::json::value::string(user_label), U(""), web::json::value::null(), web::json::value::null()); + + return nmos::control_protocol_resource({ nmos::is12_versions::v1_0, nmos::types::nc_block, std::move(data), true }); }; const auto enabled_property_descriptor = nmos::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, U("enabled"), U("NcBoolean"), false, false, false, false, web::json::value::null()); + const auto class_id_property_descriptor = nmos::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, U("classId"), U("NcClassId"), true, false, false, false, web::json::value::null()); { // Check the successful modification of the "enabled" flag of mon1's worker base class in Modify mode // @@ -417,7 +423,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -430,7 +436,7 @@ BST_TEST_CASE(testApplyBackupDataSet) // not expecting callbacks to be invoked as no read only properties, or rebuildable blocks modified BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); - BST_CHECK(!add_device_model_object_called); + BST_CHECK(!create_device_model_object_called); } const auto connection_status_property_descriptor = nmos::details::make_nc_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, U("connectionStatusMessage"), U("NcString"), true, false, false, false, web::json::value::null()); { @@ -438,7 +444,7 @@ BST_TEST_CASE(testApplyBackupDataSet) // get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; - add_device_model_object_called = false; + create_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -457,7 +463,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -471,14 +477,14 @@ BST_TEST_CASE(testApplyBackupDataSet) // but not to modify_rebuildable_block_called BST_CHECK(get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); - BST_CHECK(!add_device_model_object_called); + BST_CHECK(!create_device_model_object_called); } { // Check error generated when attempting to change a read only property of non-rebuidable object in Rebuild mode // get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; - add_device_model_object_called = false; + create_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -497,7 +503,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -521,14 +527,14 @@ BST_TEST_CASE(testApplyBackupDataSet) // but not to modify_rebuildable_block_called BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); - BST_CHECK(!add_device_model_object_called); + BST_CHECK(!create_device_model_object_called); } { // Check an error is caused by trying to modify a read only property in Modify mode // get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; - add_device_model_object_called = false; + create_device_model_object_called = false; // Change a read only property in Rebuild mode // Create Object Properties Holder @@ -549,7 +555,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -572,7 +578,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); - BST_CHECK(!add_device_model_object_called); + BST_CHECK(!create_device_model_object_called); } const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); { @@ -580,7 +586,7 @@ BST_TEST_CASE(testApplyBackupDataSet) // get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; - add_device_model_object_called = false; + create_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -597,7 +603,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -609,15 +615,15 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(remove_device_model_object_called); - BST_CHECK(!add_device_model_object_called); + BST_CHECK(!create_device_model_object_called); } const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, U("oid"), U("NcOid"), true, false, false, false, web::json::value::null()); { - // Check add_device_model_object_called is called when trying to modify a rebuildable block + // Check create_device_model_object_called is called when trying to modify a rebuildable block // get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; - add_device_model_object_called = false; + create_device_model_object_called = false; auto monitor_3_oid = 999; // Create Object Properties Holder for Block, with a Property Holder for the block members @@ -643,6 +649,7 @@ BST_TEST_CASE(testApplyBackupDataSet) { auto property_holders = value::array(); push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -651,7 +658,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors BST_REQUIRE_EQUAL(3, output.as_array().size()); @@ -681,14 +688,14 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); - BST_CHECK(add_device_model_object_called); + BST_CHECK(create_device_model_object_called); } { // Check that role paths outside of the scope of the target role path are errored // get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; - add_device_model_object_called = false; + create_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -705,14 +712,14 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation no object_properties_holders as not in the restore scope BST_REQUIRE_EQUAL(0, output.as_array().size()); BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); - BST_CHECK(!add_device_model_object_called); + BST_CHECK(!create_device_model_object_called); } } @@ -759,7 +766,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) // undefined callback stubs nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list; nmos::remove_device_model_object_handler remove_device_model_object; - nmos::add_device_model_object_handler add_device_model_object; + nmos::create_device_model_object_handler create_device_model_object; const auto enabled_property_descriptor = nmos::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, U("enabled"), U("NcBoolean"), false, false, false, false, web::json::value::null()); { @@ -779,7 +786,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -806,7 +813,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -837,7 +844,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -850,7 +857,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) { const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, U("oid"), U("NcOid"), true, false, false, false, web::json::value::null()); - // Check undefined add_device_model_object and remove_device_model_object causes an unsupported error when attempting to modify a rebuildable block + // Check undefined create_device_model_object and remove_device_model_object causes an unsupported error when attempting to modify a rebuildable block // // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -873,7 +880,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holder BST_CHECK_EQUAL(2, output.as_array().size()); @@ -939,10 +946,10 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) bool get_read_only_modification_allow_list_called = false; bool remove_device_model_object_called = false; - bool add_device_model_object_called = false; + bool create_device_model_object_called = false; // callback stubs - nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resource& resource, const std::vector& target_role_path, const const std::vector& property_ids) + nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resource& resource, const std::vector& target_role_path, const std::vector& property_ids) { get_read_only_modification_allow_list_called = true; return property_ids; @@ -955,23 +962,27 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) return true; }; - nmos::add_device_model_object_handler add_device_model_object = [&](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::create_device_model_object_handler create_device_model_object = [&](const nmos::nc_class_id& class_id, nmos::nc_oid oid, bool constant_oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, bool validate, const std::map& property_values) { - add_device_model_object_called = true; - const auto& role_path = nmos::fields::nc::path(object_properties_holder); - return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::ok, U("OK")); + using web::json::value; + + create_device_model_object_called = true; + + auto data = nmos::details::make_nc_object(nmos::nc_receiver_monitor_class_id, oid, true, owner, role, web::json::value::string(user_label), U(""), web::json::value::null(), web::json::value::null()); + + return nmos::control_protocol_resource({ nmos::is12_versions::v1_0, nmos::types::nc_block, std::move(data), true }); }; const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, U("oid"), U("NcOid"), true, false, false, false, web::json::value::null()); + const auto class_id_property_descriptor = nmos::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, U("classId"), U("NcClassId"), true, false, false, false, web::json::value::null()); { - // Check new oid is generated for new device model object + // Handle constant oid clash // get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; - add_device_model_object_called = false; + create_device_model_object_called = false; - auto monitor_3_oid = 999; // Create Object Properties Holder for Block, with a Property Holder for the block members auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers") }); @@ -980,21 +991,22 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, false, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } - // Create Object Properties Holder for new monitor const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } + // Create Object Properties Holder for new monitor const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -1003,7 +1015,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors BST_REQUIRE_EQUAL(3, output.as_array().size()); @@ -1013,46 +1025,33 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) BST_REQUIRE_EQUAL(block_object_properties_holder.size(), 1); { const auto& object_properties_set_validation = block_object_properties_holder.at(0); - BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); - // Expect a warning that the oid for mon3 has changed - BST_REQUIRE_EQUAL(1, nmos::fields::nc::notices(object_properties_set_validation).size()); - const auto& notice = nmos::fields::nc::notices(object_properties_set_validation).at(0); - BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::warning, nmos::fields::nc::notice_type(notice)); - const auto& property_id = nmos::fields::nc::id(notice); - BST_CHECK_EQUAL(nmos::fields::nc::level(property_id), nmos::nc_block_members_property_id.level); - BST_CHECK_EQUAL(nmos::fields::nc::index(property_id), nmos::nc_block_members_property_id.index); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); } const auto& monitor_2_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_2_role_path.as_array()); BST_REQUIRE_EQUAL(monitor_2_object_properties_holder.size(), 1); { const auto& object_properties_set_validation = monitor_2_object_properties_holder.at(0); BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); - BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); } const auto& monitor_3_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_3_role_path.as_array()); BST_REQUIRE_EQUAL(monitor_3_object_properties_holder.size(), 1); { const auto& object_properties_set_validation = monitor_3_object_properties_holder.at(0); - BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); - BST_CHECK_EQUAL(1, nmos::fields::nc::notices(object_properties_set_validation).size()); - const auto& notice = nmos::fields::nc::notices(object_properties_set_validation).at(0); - BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::warning, nmos::fields::nc::notice_type(notice)); - const auto& property_id = nmos::fields::nc::id(notice); - BST_CHECK_EQUAL(nmos::fields::nc::level(property_id), nmos::nc_object_oid_property_id.level); - BST_CHECK_EQUAL(nmos::fields::nc::index(property_id), nmos::nc_object_oid_property_id.index); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::device_error, nmos::fields::nc::status(object_properties_set_validation)); } BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); - BST_CHECK(add_device_model_object_called); + BST_CHECK(!create_device_model_object_called); } { - // Handle constant oid clash + // Check new oid is generated for new device model object // get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; - add_device_model_object_called = false; + create_device_model_object_called = false; + auto monitor_3_oid = 999; // Create Object Properties Holder for Block, with a Property Holder for the block members auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers") }); @@ -1061,21 +1060,22 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) auto members = value::array(); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, false, monitor_class_id, U("label"), receiver_block_oid)); push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } + // Create Object Properties Holder for new monitor const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } - // Create Object Properties Holder for new monitor const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -1084,7 +1084,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors BST_REQUIRE_EQUAL(3, output.as_array().size()); @@ -1094,24 +1094,38 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) BST_REQUIRE_EQUAL(block_object_properties_holder.size(), 1); { const auto& object_properties_set_validation = block_object_properties_holder.at(0); - BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + // Expect a warning that the oid for mon3 has changed + BST_REQUIRE_EQUAL(1, nmos::fields::nc::notices(object_properties_set_validation).size()); + const auto& notice = nmos::fields::nc::notices(object_properties_set_validation).at(0); + BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::warning, nmos::fields::nc::notice_type(notice)); + const auto& property_id = nmos::fields::nc::id(notice); + BST_CHECK_EQUAL(nmos::fields::nc::level(property_id), nmos::nc_block_members_property_id.level); + BST_CHECK_EQUAL(nmos::fields::nc::index(property_id), nmos::nc_block_members_property_id.index); } const auto& monitor_2_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_2_role_path.as_array()); BST_REQUIRE_EQUAL(monitor_2_object_properties_holder.size(), 1); { const auto& object_properties_set_validation = monitor_2_object_properties_holder.at(0); BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); } const auto& monitor_3_object_properties_holder = nmos::get_object_properties_holder(output.as_array(), monitor_3_role_path.as_array()); BST_REQUIRE_EQUAL(monitor_3_object_properties_holder.size(), 1); { const auto& object_properties_set_validation = monitor_3_object_properties_holder.at(0); - BST_CHECK_EQUAL(nmos::nc_restore_validation_status::device_error, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(2, nmos::fields::nc::notices(object_properties_set_validation).size()); + const auto& notice = nmos::fields::nc::notices(object_properties_set_validation).at(0); + BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::warning, nmos::fields::nc::notice_type(notice)); + const auto& property_id = nmos::fields::nc::id(notice); + BST_CHECK_EQUAL(nmos::fields::nc::level(property_id), nmos::nc_object_oid_property_id.level); + BST_CHECK_EQUAL(nmos::fields::nc::index(property_id), nmos::nc_object_oid_property_id.index); } BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); - BST_CHECK(!add_device_model_object_called); + BST_CHECK(create_device_model_object_called); } } @@ -1160,10 +1174,10 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) bool get_read_only_modification_allow_list_called = false; bool remove_device_model_object_called = false; - bool add_device_model_object_called = false; + bool create_device_model_object_called = false; // callback stubs - nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resource& resource, const std::vector& target_role_path, const const std::vector& property_ids) + nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resource& resource, const std::vector& target_role_path, const std::vector& property_ids) { get_read_only_modification_allow_list_called = true; return property_ids; @@ -1177,12 +1191,11 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) return false; }; - nmos::add_device_model_object_handler add_device_model_object = [&](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::create_device_model_object_handler create_device_model_object = [&](const nmos::nc_class_id& class_id, nmos::nc_oid oid, bool constant_oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, bool validate, const std::map& property_values) { - add_device_model_object_called = true; - const auto& role_path = nmos::fields::nc::path(object_properties_holder); + create_device_model_object_called = true; // Simulate error on adding object to device model - return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::device_error, U("Unable to add object to device model")); + return nmos::control_protocol_resource(); }; const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, U("oid"), U("NcOid"), true, false, false, false, web::json::value::null()); @@ -1191,7 +1204,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) // get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; - add_device_model_object_called = false; + create_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -1208,7 +1221,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -1225,14 +1238,14 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(remove_device_model_object_called); - BST_CHECK(!add_device_model_object_called); + BST_CHECK(!create_device_model_object_called); } { // Check on remove_device_model_object_called error all other object properties holders are processed // get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; - add_device_model_object_called = false; + create_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -1256,7 +1269,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders BST_CHECK_EQUAL(2, output.as_array().size()); @@ -1282,12 +1295,13 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); } } + const auto class_id_property_descriptor = nmos::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, U("classId"), U("NcClassId"), true, false, false, false, web::json::value::null()); { - // Check add_device_model_object_called error is handled when trying to modify a rebuildable block + // Check create_device_model_object_called error is handled when trying to modify a rebuildable block // get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; - add_device_model_object_called = false; + create_device_model_object_called = false; auto monitor_3_oid = 999; // Create Object Properties Holder for Block, with a Property Holder for the block members @@ -1319,6 +1333,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) { auto property_holders = value::array(); push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -1327,7 +1342,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors BST_CHECK_EQUAL(4, output.as_array().size()); @@ -1364,14 +1379,14 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) BST_CHECK(!get_read_only_modification_allow_list_called); BST_CHECK(!remove_device_model_object_called); - BST_CHECK(add_device_model_object_called); + BST_CHECK(create_device_model_object_called); } { // Check duplicate block object properties holders are handled // get_read_only_modification_allow_list_called = false; remove_device_model_object_called = false; - add_device_model_object_called = false; + create_device_model_object_called = false; // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -1401,7 +1416,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, add_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders BST_CHECK_EQUAL(3, output.as_array().size()); @@ -1470,10 +1485,10 @@ BST_TEST_CASE(testModifyRebuildableBlock) bool get_read_only_modification_allow_list_called = false; bool remove_device_model_object_called = false; - bool add_device_model_object_called = false; + bool create_device_model_object_called = false; // callback stubs - nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resource& resource, const std::vector& target_role_path, const const std::vector& property_ids) + nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resource& resource, const std::vector& target_role_path, const std::vector& property_ids) { get_read_only_modification_allow_list_called = true; return property_ids; @@ -1486,11 +1501,15 @@ BST_TEST_CASE(testModifyRebuildableBlock) return true; }; - nmos::add_device_model_object_handler add_device_model_object = [&](const web::json::value& object_properties_holder, const nmos::nc_oid oid, const nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) + nmos::create_device_model_object_handler create_device_model_object = [&](const nmos::nc_class_id& class_id, nmos::nc_oid oid, bool constant_oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, bool validate, const std::map& property_values) { - add_device_model_object_called = true; - const auto& role_path = nmos::fields::nc::path(object_properties_holder); - return nmos::make_object_properties_set_validation(role_path, nmos::nc_restore_validation_status::ok, U("OK")); + using web::json::value; + + create_device_model_object_called = true; + + auto data = nmos::details::make_nc_object(nmos::nc_receiver_monitor_class_id, oid, true, owner, role, web::json::value::string(user_label), U(""), web::json::value::null(), web::json::value::null()); + + return nmos::control_protocol_resource({ nmos::is12_versions::v1_0, nmos::types::nc_block, std::move(data), true }); }; const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, U("oid"), U("NcOid"), true, false, false, false, web::json::value::null()); @@ -1539,7 +1558,7 @@ BST_TEST_CASE(testModifyRebuildableBlock) const auto resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); // allowed member classes specified for block but no class_id property holder in the new monitor object properties holder - const auto object_set_validations = nmos::details::modify_rebuildable_block(resources, object_properties_holder_map, *resource, target_role_path.as_array(), block_object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object); + const auto object_set_validations = nmos::details::modify_rebuildable_block(resources, object_properties_holder_map, *resource, target_role_path.as_array(), block_object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, create_device_model_object); BST_REQUIRE_EQUAL(object_set_validations.size(), 2); { @@ -1598,7 +1617,7 @@ BST_TEST_CASE(testModifyRebuildableBlock) const auto resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); // allowed member classes specified for block but class_id property holder has disallowed class_id - const auto object_set_validations = nmos::details::modify_rebuildable_block(resources, object_properties_holder_map, *resource, target_role_path.as_array(), block_object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object); + const auto object_set_validations = nmos::details::modify_rebuildable_block(resources, object_properties_holder_map, *resource, target_role_path.as_array(), block_object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, create_device_model_object); BST_REQUIRE_EQUAL(object_set_validations.size(), 2); { @@ -1657,7 +1676,7 @@ BST_TEST_CASE(testModifyRebuildableBlock) const auto resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); // allowed member classes specified for block but class_id property holder has disallowed class_id - const auto object_set_validations = nmos::details::modify_rebuildable_block(resources, object_properties_holder_map, *resource, target_role_path.as_array(), block_object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, add_device_model_object); + const auto object_set_validations = nmos::details::modify_rebuildable_block(resources, object_properties_holder_map, *resource, target_role_path.as_array(), block_object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, create_device_model_object); BST_REQUIRE_EQUAL(object_set_validations.size(), 2); { From f0feec25deb15a3fb9b4120cee5de22bb8d71d28 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Thu, 17 Jul 2025 22:02:12 +0100 Subject: [PATCH 212/250] Update comments --- Development/nmos-cpp-node/node_implementation.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index d8ed8d8af..33ce702b7 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1790,14 +1790,14 @@ nmos::remove_device_model_object_handler make_remove_device_model_object_handler }; } +// This example callback shows how to add a receiver monitor resource to the device model +// The receivers block that contains the monitors must be rebuildable +// To add a monitor, restore a backup dataset including an additional monitor in the members property of the receivers block in Rebuild mode +// Also include an object properties holder for the new monitor including a touchpoint property holder refencing the NMOS Receiver resource being monitored nmos::create_device_model_object_handler make_create_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate) { return[&model, &gate](const nmos::nc_class_id& class_id, nmos::nc_oid oid, bool constant_oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, bool validate, const std::map& property_values) { - // This example callback shows how to add a receiver monitor resource to the device model - // The receivers block that contains the monitors must be rebuildable - // To add a monitor, restore a backup dataset including an additional monitor in the members property of the receivers block in Rebuild mode - // Also include an object properties holder for the new monitor including a touchpoint property holder refencing the NMOS Receiver resource being monitored if (touchpoints.size() != 1) { slog::log(gate, SLOG_FLF) << "Either zero or more than one touchpoint found (ambiguous) when attempting to create " << role; From c9ecc667cfba36d7184fb96b4453ba1aab3236ac Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 18 Jul 2025 11:05:31 +0100 Subject: [PATCH 213/250] Update comments --- Development/nmos-cpp-node/node_implementation.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 33ce702b7..9b25ce279 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1745,6 +1745,7 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control // Example Device Configuration callbacks called when a rebuildable object is modified in Rebuild mode. +// IS-14 Device Configuration callback // This function is called when the Device Configuration API is attempting to modify // the read only properties of a rebuildable Device Model object. This callback returns an "allow list" of property ids // for properties that can be updated - the "allowed" read only property will be updated according to backup dataset received. @@ -1775,6 +1776,7 @@ nmos::get_read_only_modification_allow_list_handler make_get_read_only_modificat }; } +// IS-14 Device Configuration callback // This function is called before an object is deleted from the device model. // If this function returns true and validate is false then the object will be deleted. // If thus function returns false or validate is true then the object will not be deleted. @@ -1790,10 +1792,11 @@ nmos::remove_device_model_object_handler make_remove_device_model_object_handler }; } -// This example callback shows how to add a receiver monitor resource to the device model -// The receivers block that contains the monitors must be rebuildable -// To add a monitor, restore a backup dataset including an additional monitor in the members property of the receivers block in Rebuild mode -// Also include an object properties holder for the new monitor including a touchpoint property holder refencing the NMOS Receiver resource being monitored +// IS-14 Device Configuration callback +// This function is called when an object is to be created. +// The returned object is then added to the Device Model +// This example shows the creation of a receiver monitor resource +// In the Device Model the receivers block that contains the monitors must be rebuildable nmos::create_device_model_object_handler make_create_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate) { return[&model, &gate](const nmos::nc_class_id& class_id, nmos::nc_oid oid, bool constant_oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, bool validate, const std::map& property_values) From 0dc51e3cbed027a89ab74b7ed37512db91333487 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 18 Jul 2025 11:05:53 +0100 Subject: [PATCH 214/250] Fix create device model object validation --- Development/nmos/configuration_utils.cpp | 27 ++++++------------------ 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index de3052dd0..b6551351e 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -450,11 +450,14 @@ namespace nmos } } - // Add object to device model - nmos::nc::insert_resource(resources, std::move(device_model_object)); + if (!validate) + { + // Add object to device model + nmos::nc::insert_resource(resources, std::move(device_model_object)); - auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(block_member_description, role, oid, constant_oid, nmos::nc_receiver_monitor_class_id, block_member_user_label, owner); - members_to_add.push_back(block_member_descriptor); + auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(block_member_description, role, oid, constant_oid, nmos::nc_receiver_monitor_class_id, block_member_user_label, owner); + members_to_add.push_back(block_member_descriptor); + } web::json::push_back(object_properties_set_validations, nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok, added_object_notices.as_array())); } else @@ -462,22 +465,6 @@ namespace nmos // An error has occurred web::json::push_back(object_properties_set_validations, nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::device_error, U("Unable to create Device Model object. This may be due to missing property values."))); } - - - - //// Add warnings about known inconsistancies between backup dataset and new device model object - //for (const auto& added_object_notice: added_object_notices.as_array()) - //{ - // web::json::push_back(nmos::fields::nc::notices(object_properties_set_validation), added_object_notice); - //} - - //// If the status is anything other than OK assume the object wasn't created - //if (nmos::fields::nc::status(object_properties_set_validation) == nmos::nc_restore_validation_status::ok && !validate) - //{ - //} - - //web::json::push_back(object_properties_set_validations, object_properties_set_validation); - // erase object from object_properties_holder_map so it isn't processed subsequently object_properties_holder_map.erase(child_role_path.as_array()); } From 96d6d16434f42a5746f5a8f1c046bae89133d249 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 18 Jul 2025 11:10:21 +0100 Subject: [PATCH 215/250] Remove duplicate OID warning --- Development/nmos/configuration_utils.cpp | 3 --- Development/nmos/test/configuration_utils_test.cpp | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index b6551351e..8a022d2d5 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -312,9 +312,6 @@ namespace nmos oid = ++max_oid; const auto block_notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Dynamically generating new OID for new block member.")); web::json::push_back(block_notices, block_notice); - - const auto added_object_notice = nmos::details::make_nc_property_restore_notice(nmos::nc_object_oid_property_id, U("oid"), nmos::nc_property_restore_notice_type::warning, U("Dynamically generating new OID for new object.")); - web::json::push_back(added_object_notices, added_object_notice); } // The values in the block member object properties holder will take precidence over the block member descriptor values diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index ad8cb4d20..bef6eede0 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -1115,7 +1115,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) { const auto& object_properties_set_validation = monitor_3_object_properties_holder.at(0); BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); - BST_CHECK_EQUAL(2, nmos::fields::nc::notices(object_properties_set_validation).size()); + BST_CHECK_EQUAL(1, nmos::fields::nc::notices(object_properties_set_validation).size()); const auto& notice = nmos::fields::nc::notices(object_properties_set_validation).at(0); BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::warning, nmos::fields::nc::notice_type(notice)); const auto& property_id = nmos::fields::nc::id(notice); From 07d857b3090324b5b62660eb0eba385a65b4775a Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 18 Jul 2025 11:44:30 +0100 Subject: [PATCH 216/250] Updated remove_device_model_object_handler definition --- .../nmos-cpp-node/node_implementation.cpp | 5 +++-- Development/nmos/configuration_handlers.h | 4 ++-- Development/nmos/configuration_utils.cpp | 20 ++++++++++++------- .../nmos/test/configuration_utils_test.cpp | 8 ++++---- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 9b25ce279..77705e6a9 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1783,10 +1783,11 @@ nmos::get_read_only_modification_allow_list_handler make_get_read_only_modificat // If this function returns false an appropriate error will be passed to the calling client. nmos::remove_device_model_object_handler make_remove_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate) { - return [&model, &gate](const nmos::nc_oid oid, bool validate) + return [&model, &gate](const nmos::resource& resource, const std::vector& role_path, bool validate) { // Perform application code functions here - // oid - oid of Device Model resource beng deleted + // resource - device model object about to be deleted + // role_path - role path of device object about to be deleted // validate - true when only checks are performed, false when checks and deletion are performed return true; }; diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h index 0e52f237d..c4aafd8f3 100644 --- a/Development/nmos/configuration_handlers.h +++ b/Development/nmos/configuration_handlers.h @@ -20,12 +20,12 @@ namespace nmos } // This callback is invoked if attempting to modify read only properties when restoring a configuration. // This function should modify the Device Model object directly and return a corresponding NcObjectPropertiesSetValidation object - typedef std::function(const nmos::resource& resource, const std::vector& target_role_path, const std::vector& property_ids)> get_read_only_modification_allow_list_handler; + typedef std::function(const nmos::resource& resource, const std::vector& role_path, const std::vector& property_ids)> get_read_only_modification_allow_list_handler; // This callback is invoked if attempting to remove a device model object when restoring a configuration. // This function should handle the modification of the Device Model and any corresponding NMOS resources // and return true if successful and false otherwise - typedef std::function remove_device_model_object_handler; + typedef std::function& role_path, bool validate)> remove_device_model_object_handler; // This callback is invoked if attempting to add a device model object to a rebuildable block when restoring a configuration. // This function should handle the modification of the Device Model and any corresponding NMOS resources diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 8a022d2d5..e8a426c4a 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -206,6 +206,19 @@ namespace nmos if (resources.end() != found) { + // callback to user code + std::vector child_role_path_array; + for (const auto& element: child_role_path.as_array()) + { + child_role_path_array.push_back(element.as_string()); + } + if (!remove_device_model_object(*found, child_role_path_array, validate)) + { + // error in user code + web::json::push_back(block_notices, nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Application error."))); + continue; + } + if (!validate) // If validate is true then delete the object, just indicate whether it's possible given the data supplied { auto erase_count = nmos::nc::erase_resource(resources, found->id); @@ -217,15 +230,8 @@ namespace nmos { // unable to delete resource so report the error and don't update block web::json::push_back(block_notices, nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Unable to delete resource in Device Model."))); - continue; } } - // callback to user code - if (!remove_device_model_object(nmos::fields::nc::oid(reference_member), validate)) - { - // error in user code - web::json::push_back(block_notices, nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Application error."))); - } } else { diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index bef6eede0..96bc97436 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -386,7 +386,7 @@ BST_TEST_CASE(testApplyBackupDataSet) return property_ids; }; - nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::nc_oid reference_oid, bool validate) + nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::resource& resource, const std::vector& target_role_path, bool validate) { remove_device_model_object_called = true; @@ -955,7 +955,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) return property_ids; }; - nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::nc_oid reference_oid, bool validate) + nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::resource& resource, const std::vector& target_role_path, bool validate) { remove_device_model_object_called = true; @@ -1183,7 +1183,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) return property_ids; }; - nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::nc_oid reference_oid, bool validate) + nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::resource& resource, const std::vector& target_role_path, bool validate) { remove_device_model_object_called = true; @@ -1494,7 +1494,7 @@ BST_TEST_CASE(testModifyRebuildableBlock) return property_ids; }; - nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::nc_oid reference_oid, bool validate) + nmos::remove_device_model_object_handler remove_device_model_object = [&](const nmos::resource& resource, const std::vector& target_role_path, bool validate) { remove_device_model_object_called = true; From b2899b57538f0697834ea82809e215ef6ed8729a Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 18 Jul 2025 12:07:41 +0100 Subject: [PATCH 217/250] Update comments and tidy-up --- Development/nmos-cpp-node/node_implementation.cpp | 2 +- Development/nmos/configuration_api.cpp | 2 +- Development/nmos/configuration_handlers.h | 3 +-- Development/nmos/configuration_methods.cpp | 1 - Development/nmos/control_protocol_resource.cpp | 2 +- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 77705e6a9..dbb0c84ff 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1779,7 +1779,7 @@ nmos::get_read_only_modification_allow_list_handler make_get_read_only_modificat // IS-14 Device Configuration callback // This function is called before an object is deleted from the device model. // If this function returns true and validate is false then the object will be deleted. -// If thus function returns false or validate is true then the object will not be deleted. +// If this function returns true/false or validate is true then the object will not be deleted. // If this function returns false an appropriate error will be passed to the calling client. nmos::remove_device_model_object_handler make_remove_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate) { diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 9d3887371..1b0618e00 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -527,7 +527,7 @@ namespace nmos // do method arguments constraints validation nc::method_parameters_contraints_validation(arguments, nc_method_descriptor, get_control_protocol_datatype_descriptor); - // execute the relevant control method handler, then accumulating up their response to reponses + // execute the relevant control method handler, then accumulating up their response to responses method_result = control_method_handler(resources, *resource, arguments, nmos::fields::nc::is_deprecated(nc_method_descriptor), gate); auto status = nmos::fields::nc::status(method_result); diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h index c4aafd8f3..32286315f 100644 --- a/Development/nmos/configuration_handlers.h +++ b/Development/nmos/configuration_handlers.h @@ -2,7 +2,6 @@ #define NMOS_CONFIGURATION_HANDLERS_H #include -#include "nmos/control_protocol_typedefs.h" #include "nmos/control_protocol_handlers.h" #include "nmos/resources.h" #include "nmos/control_protocol_resource.h" @@ -29,7 +28,7 @@ namespace nmos // This callback is invoked if attempting to add a device model object to a rebuildable block when restoring a configuration. // This function should handle the modification of the Device Model and any corresponding NMOS resources - // and return correpsonding NcObjectPropertiesSetValidation objects for the object added + // and return corresponding NcObjectPropertiesSetValidation objects for the object added typedef std::function& property_values)> create_device_model_object_handler; } diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index e537062a5..b94112701 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -117,7 +117,6 @@ namespace nmos web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, bool include_descriptors, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { using web::json::value; - using web::json::value_of; value object_properties_holders = value::array(); diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index ba1412ce3..d323a2501 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -714,7 +714,7 @@ namespace nmos // IS-14 metadata fields // These fields are "invisible" as they are not part of the NcObject definition - // use make_rebuildable function to declare an control protocl resource rebuildable + // use make_rebuildable function to declare an control protocol resource rebuildable data[nmos::fields::nc::is_rebuildable] = value::boolean(false); // use allowed_member_classes to restrict the types of object that an NcBlock can contain data[nmos::fields::nc::allowed_members_classes] = value::array(); From bfe9a9af05a03817ddcac1d69d58439d56561a0a Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 18 Jul 2025 12:24:56 +0100 Subject: [PATCH 218/250] Should be value rather than reference --- Development/nmos/configuration_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index e8a426c4a..c48ba6421 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -106,7 +106,7 @@ namespace nmos read_only_property_ids.push_back(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value))); } - const auto& allow_list_read_only_property_ids = get_read_only_modification_allow_list(resource, target_role_path_array, read_only_property_ids); + const auto allow_list_read_only_property_ids = get_read_only_modification_allow_list(resource, target_role_path_array, read_only_property_ids); const auto& allowed_property_values = boost::copy_range>(filtered_property_values | boost::adaptors::filtered([&property_restore_notices, get_control_protocol_class_descriptor, class_id, allow_list_read_only_property_ids](const web::json::value& property_value) From d4e32bc2f49a69e2fcaeecb40416be1f47e94ee2 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 18 Jul 2025 12:35:50 +0100 Subject: [PATCH 219/250] Should be value than reference --- Development/nmos/configuration_methods.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index b94112701..b1dc07809 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -137,7 +137,7 @@ namespace nmos // Do something with validation fingerprint? const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } @@ -147,7 +147,7 @@ namespace nmos // Do something with validation fingerprint? const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto& object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } From cc90f53dce11336c91cc510b5cda348e2d7265c4 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 18 Jul 2025 14:24:49 +0100 Subject: [PATCH 220/250] Use value instead of reference --- Development/nmos/configuration_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index c48ba6421..303252b2c 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -723,7 +723,7 @@ namespace nmos { // Process this block to add / remove device model objects as members of this block // the object properties holder for any added objects will be erased from the object_properties_holder_map to avoid double processing - const auto& child_object_properties_set_validations = details::modify_rebuildable_block(resources, object_properties_holder_map, *r, role_path, object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, create_device_model_object); + const auto child_object_properties_set_validations = details::modify_rebuildable_block(resources, object_properties_holder_map, *r, role_path, object_properties_holder, validate, get_control_protocol_class_descriptor, remove_device_model_object, create_device_model_object); for (const auto& validation_values : child_object_properties_set_validations.as_array()) { web::json::push_back(object_properties_set_validation_values, validation_values); From e8a8189648d1c58c2882b84df07ea9ebf16476a3 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 18 Jul 2025 14:26:19 +0100 Subject: [PATCH 221/250] Replace with defined value `nc_block_members_property_id` --- Development/nmos/configuration_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 303252b2c..bf1bf236d 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -598,7 +598,7 @@ namespace nmos const auto& block_members_properties_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) | boost::adaptors::filtered([](const web::json::value& property_holder) { - return nmos::nc_property_id(2, 2) == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)); + return nmos::nc_block_members_property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)); }) ); // There should only be a single property holder for the members From b063d88f324a22b7148412996d09131cec5f2597 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 18 Jul 2025 18:08:54 +0100 Subject: [PATCH 222/250] Minor tidy-up --- .../nmos-cpp-node/node_implementation.cpp | 3 ++ Development/nmos/configuration_utils.cpp | 43 ++++++++++--------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index dbb0c84ff..ef7586c3b 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1758,6 +1758,9 @@ nmos::get_read_only_modification_allow_list_handler make_get_read_only_modificat std::vector allow_list; + // "structural properties such as classId, role, owner can only be changed when the containing parent block object is rebuildable." + // see https://specs.amwa.tv/is-14/branches/v1.0-dev/docs/Backup_&_restore.html#general-concepts + // hmm, the following should be filtered inside the nmos-cpp framework? for (const auto& property_id : property_ids) { if (property_id == nmos::nc_object_oid_property_id || property_id == nmos::nc_object_constant_oid_property_id diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index bf1bf236d..7ecbc8396 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -189,7 +189,7 @@ namespace nmos // Iterate through the members of the block and compare to the members in the backup dataset for (const auto& reference_member : reference_members) { - auto child_role_path = web::json::value_from_elements(target_role_path); + auto child_role_path = target_role_path; web::json::push_back(child_role_path, nmos::fields::nc::role(reference_member)); const auto& filtered_members = boost::copy_range>(restore_members.as_array() @@ -201,17 +201,17 @@ namespace nmos if (filtered_members.size() != 1) { // can't find this role in restore dataset, so member has been removed - // get the receiver monitor resource + + // get the associated resource to remove auto found = nmos::find_resource(resources, utility::conversions::details::to_string_t(nmos::fields::nc::oid(reference_member))); if (resources.end() != found) { // callback to user code - std::vector child_role_path_array; - for (const auto& element: child_role_path.as_array()) + const auto child_role_path_array = boost::copy_range>(child_role_path | boost::adaptors::transformed([](const web::json::value& element) { - child_role_path_array.push_back(element.as_string()); - } + return element.as_string(); + })); if (!remove_device_model_object(*found, child_role_path_array, validate)) { // error in user code @@ -243,7 +243,7 @@ namespace nmos for (const auto& restore_member : restore_members.as_array()) { - auto child_role_path = web::json::value_from_elements(target_role_path); + auto child_role_path = target_role_path; web::json::push_back(child_role_path, nmos::fields::nc::role(restore_member)); const auto& filtered_members = boost::copy_range>(reference_members @@ -255,17 +255,18 @@ namespace nmos if (filtered_members.size() != 1) { // can't find this role in existing members, so member need to be added - // Find the object_properties_holder that describes the receiver monitor object - if (object_properties_holder_map.find(child_role_path.as_array()) == object_properties_holder_map.end()) + + // find the object_properties_holder that describes the object + if (object_properties_holder_map.find(child_role_path) == object_properties_holder_map.end()) { auto status_message = U("Cannot find NcObjectPropertiesHolder for new resource"); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, status_message); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::failed, status_message); web::json::push_back(object_properties_set_validations, object_properties_set_validation); continue; } - const auto& child_object_properties_holder = object_properties_holder_map.find(child_role_path.as_array()); + const auto& child_object_properties_holder = object_properties_holder_map.find(child_role_path); // Get member descriptor properties auto role = nmos::fields::nc::role(restore_member); @@ -297,13 +298,13 @@ namespace nmos // oid already in use! // create device error for new object auto status_message = U("Constant OID error. OID already in use"); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::device_error, status_message); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::device_error, status_message); web::json::push_back(object_properties_set_validations, object_properties_set_validation); // also create error notice for the block const auto block_notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, status_message); web::json::push_back(block_notices, block_notice); // erase object from object_properties_holder_map so it isn't processed subsequently - object_properties_holder_map.erase(child_role_path.as_array()); + object_properties_holder_map.erase(child_role_path); continue; } } @@ -366,14 +367,14 @@ namespace nmos ss << U("Class ID property value holder missing for role=") << role; const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("class_id"), nmos::nc_property_restore_notice_type::error, ss.str()); web::json::push_back(added_object_notices, notice); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, added_object_notices.as_array(), ss.str()); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::failed, added_object_notices.as_array(), ss.str()); web::json::push_back(object_properties_set_validations, object_properties_set_validation); // also create error notice for the block const auto block_notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, ss.str()); web::json::push_back(block_notices, block_notice); // erase object from object_properties_holder_map so it isn't processed subsequently - object_properties_holder_map.erase(child_role_path.as_array()); + object_properties_holder_map.erase(child_role_path); continue; } @@ -390,21 +391,21 @@ namespace nmos }) ); - // If receiver monitor class not allowed then return with error + // If the class not allowed then return with error if (filtered_classes.size() == 0) { utility::stringstream_t ss; ss << U("Device model error: attempting to add unexpected class for role=") << role; const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("class_id"), nmos::nc_property_restore_notice_type::error, ss.str()); web::json::push_back(added_object_notices, notice); - auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::failed, added_object_notices.as_array(), ss.str()); + auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::failed, added_object_notices.as_array(), ss.str()); web::json::push_back(object_properties_set_validations, object_properties_set_validation); // also create error notice for the block const auto block_notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, ss.str()); web::json::push_back(block_notices, block_notice); // erase object from object_properties_holder_map so it isn't processed subsequently - object_properties_holder_map.erase(child_role_path.as_array()); + object_properties_holder_map.erase(child_role_path); continue; } @@ -461,15 +462,15 @@ namespace nmos auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(block_member_description, role, oid, constant_oid, nmos::nc_receiver_monitor_class_id, block_member_user_label, owner); members_to_add.push_back(block_member_descriptor); } - web::json::push_back(object_properties_set_validations, nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::ok, added_object_notices.as_array())); + web::json::push_back(object_properties_set_validations, nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::ok, added_object_notices.as_array())); } else { // An error has occurred - web::json::push_back(object_properties_set_validations, nmos::make_object_properties_set_validation(child_role_path.as_array(), nmos::nc_restore_validation_status::device_error, U("Unable to create Device Model object. This may be due to missing property values."))); + web::json::push_back(object_properties_set_validations, nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::device_error, U("Unable to create Device Model object. This may be due to missing property values."))); } // erase object from object_properties_holder_map so it isn't processed subsequently - object_properties_holder_map.erase(child_role_path.as_array()); + object_properties_holder_map.erase(child_role_path); } } // If there are any error notices, then give an overall error status for object properties set validation From dac85d3860c08aa553d8d173843e930bfc7a7fdb Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Mon, 21 Jul 2025 11:48:41 +0100 Subject: [PATCH 223/250] Add in create and validate validation fingerprint callbacks --- Development/nmos-cpp-node/main.cpp | 2 +- .../nmos-cpp-node/node_implementation.cpp | 44 ++++++------ Development/nmos/configuration_api.cpp | 42 +++++------ Development/nmos/configuration_api.h | 2 +- Development/nmos/configuration_handlers.h | 16 +++-- Development/nmos/configuration_methods.cpp | 70 +++++++------------ Development/nmos/configuration_methods.h | 6 +- Development/nmos/configuration_utils.cpp | 11 ++- Development/nmos/control_protocol_state.cpp | 26 +++---- Development/nmos/control_protocol_state.h | 2 +- Development/nmos/node_server.cpp | 2 +- Development/nmos/node_server.h | 8 ++- .../nmos/test/configuration_methods_test.cpp | 23 +++++- .../nmos/test/configuration_utils_test.cpp | 6 +- 14 files changed, 144 insertions(+), 116 deletions(-) diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index f269fb09e..27d2f9c87 100644 --- a/Development/nmos-cpp-node/main.cpp +++ b/Development/nmos-cpp-node/main.cpp @@ -138,7 +138,7 @@ int main(int argc, char* argv[]) .on_request_authorization_code(nmos::experimental::make_request_authorization_code_handler(gate)); // may be omitted, only required for OAuth client which is using the Authorization Code Flow to obtain the access token } - nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.get_read_only_modification_allow_list, node_implementation.remove_device_model_object, node_implementation.create_device_model_object); + nmos::experimental::control_protocol_state control_protocol_state(node_implementation.control_protocol_property_changed, node_implementation.create_validation_fingerprint, node_implementation.validate_validation_fingerprint, node_implementation.get_read_only_modification_allow_list, node_implementation.remove_device_model_object, node_implementation.create_device_model_object); if (0 <= nmos::fields::control_protocol_ws_port(node_model.settings)) { node_implementation diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index ef7586c3b..3f8a78099 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1745,6 +1745,26 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control // Example Device Configuration callbacks called when a rebuildable object is modified in Rebuild mode. +// IS-14 Device Configuration callback +// This function should generate a fingerprint that can be used for subsequent validation. +nmos::create_validation_fingerprint_handler make_create_validation_fingerprint_handler(const nmos::resources& resources, slog::base_gate& gate) +{ + return [&resources, &gate](const nmos::resources& resources, const nmos::resource& resource) + { + return U("Sony nmos-cpp node"); + }; +} + +// IS-14 Device Configuration callback +// This function called by a validate or restore and can be used to validate a validation fingerprint. Returning false will fail the validate or restore operation. +nmos::validate_validation_fingerprint_handler make_validate_validation_fingerprint_handler(const nmos::resources& resources, slog::base_gate& gate) +{ + return [&resources, &gate](const nmos::resources& resources, const nmos::resource& resource, const utility::string_t& validation_fingerprint) + { + return true; + }; +} + // IS-14 Device Configuration callback // This function is called when the Device Configuration API is attempting to modify // the read only properties of a rebuildable Device Model object. This callback returns an "allow list" of property ids @@ -1756,26 +1776,8 @@ nmos::get_read_only_modification_allow_list_handler make_get_read_only_modificat // Use this function to create allow list of property ids for properties in the object should be modified by the configuration API slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do filter_property_holders"; - std::vector allow_list; - - // "structural properties such as classId, role, owner can only be changed when the containing parent block object is rebuildable." - // see https://specs.amwa.tv/is-14/branches/v1.0-dev/docs/Backup_&_restore.html#general-concepts - // hmm, the following should be filtered inside the nmos-cpp framework? - for (const auto& property_id : property_ids) - { - if (property_id == nmos::nc_object_oid_property_id || property_id == nmos::nc_object_constant_oid_property_id - || property_id == nmos::nc_object_role_property_id || property_id == nmos::nc_object_class_id_property_id - || property_id == nmos::nc_object_owner_property_id) - { - // don't modify this property - } - else - { - // allow modification of this read only property - allow_list.push_back(property_id); - } - } - return allow_list; + // Filter out any read only properties that should not be modified + return property_ids; }; } @@ -1972,6 +1974,8 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode .on_validate_channelmapping_output_map(make_node_implementation_map_validator()) // may be omitted if not required .on_channelmapping_activated(make_node_implementation_channelmapping_activation_handler(gate)) .on_control_protocol_property_changed(make_node_implementation_control_protocol_property_changed_handler(gate)) // may be omitted if IS-12 not required + .on_create_validation_fingerprint(make_create_validation_fingerprint_handler(model.control_protocol_resources, gate)) + .on_validate_validation_fingerprint(make_validate_validation_fingerprint_handler(model.control_protocol_resources, gate)) .on_get_read_only_modification_allow_list(make_get_read_only_modification_allow_list_handler(model.control_protocol_resources, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required .on_remove_device_model_object(make_remove_device_model_object_handler(model, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required .on_create_device_model_object(make_create_device_model_object_handler(model, gate)); // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 1b0618e00..b98902c93 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -18,9 +18,9 @@ namespace nmos { - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, create_validation_fingerprint_handler create_validation_fingerprint, validate_validation_fingerprint_handler validate_validation_fingerprint, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + web::http::experimental::listener::api_router make_configuration_api(node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, create_validation_fingerprint_handler create_validation_fingerprint, validate_validation_fingerprint_handler validate_validation_fingerprint, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -51,7 +51,7 @@ namespace nmos return pplx::task_from_result(true); }); - configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object, property_changed, gate)); + configuration_api.mount(U("/x-nmos/") + nmos::patterns::configuration_api.pattern + U("/") + nmos::patterns::version.pattern, make_unmounted_configuration_api(model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor, create_validation_fingerprint, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object, property_changed, gate)); return configuration_api; } @@ -160,7 +160,7 @@ namespace nmos } } - inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) + inline web::http::experimental::listener::api_router make_unmounted_configuration_api(node_model& model, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, create_validation_fingerprint_handler create_validation_fingerprint, validate_validation_fingerprint_handler validate_validation_fingerprint, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate_) { using namespace web::http::experimental::listener::api_router_using_declarations; @@ -532,9 +532,9 @@ namespace nmos auto status = nmos::fields::nc::status(method_result); if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } - else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } - else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } - else { code = status_codes::InternalError; } + else if (status / 100 == 4) { code = status_codes::BadRequest; } // 4xx error + else if (status / 100 == 5) { code = status_codes::InternalError; } // 5xx error + else { code = status; } } catch (const nmos::control_protocol_exception& e) { @@ -625,7 +625,7 @@ namespace nmos }); // GET /rolePaths/{rolePath}/bulkProperties - invokes get_properties_by_path method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::GET, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::GET, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, create_validation_fingerprint, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); @@ -643,13 +643,13 @@ namespace nmos bool recurse = details::parse_recurse_query_parameter(req.request_uri().query()); bool include_descriptors = details::parse_include_descriptors_query_parameter(req.request_uri().query()); - method_result = get_properties_by_path(resources, *resource, recurse, include_descriptors, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + method_result = get_properties_by_path(resources, *resource, recurse, include_descriptors, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, create_validation_fingerprint); auto status = nmos::fields::nc::status(method_result); if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } - else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } - else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } - else { code = status_codes::InternalError; } + else if (status / 100 == 4) { code = status_codes::BadRequest; } // 4xx error + else if (status / 100 == 5) { code = status_codes::InternalError; } // 5xx error + else { code = status; } } catch (const nmos::control_protocol_exception& e) { @@ -673,12 +673,12 @@ namespace nmos }); // PATCH /rolePaths/{rolePath}/bulkProperties - invokes validation_set_properties_by_path method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, validate_validation_fingerprint, remove_device_model_object, create_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object, version, &gate_](value body) mutable + return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, validate_validation_fingerprint, remove_device_model_object, create_device_model_object, version, &gate_](value body) mutable { auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; @@ -698,13 +698,13 @@ namespace nmos const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); const auto& backup_data_set = nmos::fields::nc::data_set(arguments); - method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); auto status = nmos::fields::nc::status(method_result); if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } - else if (nc_method_status::parameter_error == status) { code = status_codes::BadRequest; } - else if (nc_method_status::device_error == status) { code = status_codes::InternalError; } - else { code = status_codes::InternalError; } + else if (status / 100 == 4) { code = status_codes::BadRequest; } // 4xx error + else if (status / 100 == 5) { code = status_codes::InternalError; } // 5xx error + else { code = status; } } catch (const nmos::control_protocol_exception& e) { @@ -737,12 +737,12 @@ namespace nmos }); // PUT /rolePaths/{rolePath}/bulkProperties - invokes set_properties_by_path method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object, version, &gate_](value body) mutable + return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object, version, &gate_](value body) mutable { auto lock = model.write_lock(); auto& resources = model.control_protocol_resources; @@ -762,7 +762,7 @@ namespace nmos const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); const auto& backup_data_set = nmos::fields::nc::data_set(arguments); - method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); code = status_codes::OK; diff --git a/Development/nmos/configuration_api.h b/Development/nmos/configuration_api.h index 91e7f14b7..c11b16082 100644 --- a/Development/nmos/configuration_api.h +++ b/Development/nmos/configuration_api.h @@ -16,7 +16,7 @@ namespace nmos { struct node_model; - web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + web::http::experimental::listener::api_router make_configuration_api(nmos::node_model& model, web::http::experimental::listener::route_handler validate_authorization, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, create_validation_fingerprint_handler create_validation_fingerprint, validate_validation_fingerprint_handler validate_validation_fingerprint, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); } #endif diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h index 32286315f..84f5df1b9 100644 --- a/Development/nmos/configuration_handlers.h +++ b/Development/nmos/configuration_handlers.h @@ -17,18 +17,24 @@ namespace nmos { struct control_protocol_state; } + // This callback is invoked when getting a backup dataset to generate a validation fingerprint + // This function should generate a fingerprint that can be used for subsequent validation + typedef std::function create_validation_fingerprint_handler; + + // This callback is invoked when validating or restoring a backup dataset + // This function should validate the validation fingerprint + typedef std::function validate_validation_fingerprint_handler; + // This callback is invoked if attempting to modify read only properties when restoring a configuration. - // This function should modify the Device Model object directly and return a corresponding NcObjectPropertiesSetValidation object + // This function return a vector of property ids of the read only properties allowed to be modified typedef std::function(const nmos::resource& resource, const std::vector& role_path, const std::vector& property_ids)> get_read_only_modification_allow_list_handler; // This callback is invoked if attempting to remove a device model object when restoring a configuration. - // This function should handle the modification of the Device Model and any corresponding NMOS resources - // and return true if successful and false otherwise + // This function calls back before an object is removed from the device model. typedef std::function& role_path, bool validate)> remove_device_model_object_handler; // This callback is invoked if attempting to add a device model object to a rebuildable block when restoring a configuration. - // This function should handle the modification of the Device Model and any corresponding NMOS resources - // and return corresponding NcObjectPropertiesSetValidation objects for the object added + // This function should return the newly created device model object. typedef std::function& property_values)> create_device_model_object_handler; } diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index b1dc07809..3081d7386 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -78,43 +78,9 @@ namespace nmos } } } - - 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::nc::is_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; - } } - web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, bool include_descriptors, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) + web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, bool include_descriptors, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::create_validation_fingerprint_handler create_validation_fingerprint) { using web::json::value; @@ -122,19 +88,29 @@ namespace nmos details::populate_object_property_holder(resources, get_control_protocol_class_descriptor, resource, recurse, include_descriptors, object_properties_holders); - size_t validation_fingerprint = details::generate_validation_fingerprint(resources, resource); + utility::string_t validation_fingerprint = U(""); - utility::ostringstream_t ss; - ss << validation_fingerprint; + if (create_validation_fingerprint) + { + validation_fingerprint = create_validation_fingerprint(resources, resource); + } - auto bulk_properties_holder = nmos::details::make_nc_bulk_properties_holder(ss.str(), object_properties_holders); + auto bulk_properties_holder = nmos::details::make_nc_bulk_properties_holder(validation_fingerprint, object_properties_holders); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, bulk_properties_holder); } - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) { - // Do something with validation fingerprint? + if (validate_validation_fingerprint) + { + const auto& validation_fingerprint = nmos::fields::nc::validation_fingerprint(backup_data_set); + + if (!validate_validation_fingerprint(resources, resource, validation_fingerprint.c_str())) + { + return nmos::details::make_nc_method_result_error({ nmos::nc_method_status::invalid_request }, U("Invalid validation fingerprint")); + } + } const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); const auto object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); @@ -142,9 +118,17 @@ namespace nmos return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } - web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) { - // Do something with validation fingerprint? + if (validate_validation_fingerprint) + { + const auto& validation_fingerprint = nmos::fields::nc::validation_fingerprint(backup_data_set); + + if (!validate_validation_fingerprint(resources, resource, validation_fingerprint.c_str())) + { + return nmos::details::make_nc_method_result_error({ nmos::nc_method_status::invalid_request }, U("Invalid validation fingerprint")); + } + } const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); const auto object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); diff --git a/Development/nmos/configuration_methods.h b/Development/nmos/configuration_methods.h index cc0af336d..cc351077a 100644 --- a/Development/nmos/configuration_methods.h +++ b/Development/nmos/configuration_methods.h @@ -15,11 +15,11 @@ namespace nmos struct control_protocol_resource; // Implementation of IS-14 function for creating backup dataset from a Device Model - web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, bool include_descriptors, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor); + web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, bool include_descriptors, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::create_validation_fingerprint_handler create_validation_fingerprint); - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); - web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); } #endif diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 7ecbc8396..40d8aef5b 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -103,7 +103,16 @@ namespace nmos std::vector read_only_property_ids; for (const auto& property_value: read_only_property_values) { - read_only_property_ids.push_back(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value))); + const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + // Don't include structural properties - changing these could break the device model + if (property_id != nmos::nc_object_class_id_property_id && + property_id != nmos::nc_object_oid_property_id && + property_id != nmos::nc_object_constant_oid_property_id && + property_id != nmos::nc_object_owner_property_id && + property_id != nmos::nc_object_role_property_id) + { + read_only_property_ids.push_back(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value))); + } } const auto allow_list_read_only_property_ids = get_read_only_modification_allow_list(resource, target_role_path_array, read_only_property_ids); diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 7b23600e8..d7d5b81b7 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -180,19 +180,19 @@ namespace nmos return get_datatype(arguments, is_deprecated, get_control_protocol_datatype_descriptor, gate); }; } - nmos::experimental::control_protocol_method_handler make_nc_get_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) + nmos::experimental::control_protocol_method_handler make_nc_get_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, create_validation_fingerprint_handler create_validation_fingerprint) { - return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, create_validation_fingerprint](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); bool include_descriptors = nmos::fields::nc::include_descriptors(arguments); - return nmos::get_properties_by_path(resources, resource, recurse, include_descriptors, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + return nmos::get_properties_by_path(resources, resource, recurse, include_descriptors, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, create_validation_fingerprint); }; } - nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, create_device_model_object_handler create_device_model_object) + nmos::experimental::control_protocol_method_handler make_nc_validate_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, validate_validation_fingerprint_handler validate_validation_fingerprint, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, create_device_model_object_handler create_device_model_object) { - return [&get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [&get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, validate_validation_fingerprint, remove_device_model_object, create_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -206,7 +206,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); if (get_read_only_modification_allow_list && remove_device_model_object && create_device_model_object) { - result = validate_set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + result = validate_set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -217,9 +217,9 @@ namespace nmos return result; }; } - nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, create_device_model_object_handler create_device_model_object) + nmos::experimental::control_protocol_method_handler make_nc_set_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, validate_validation_fingerprint_handler validate_validation_fingerprint, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, create_device_model_object_handler create_device_model_object) { - return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { bool recurse = nmos::fields::nc::recurse(arguments); const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); @@ -233,7 +233,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("callbacks not implemented")); if (get_read_only_modification_allow_list && remove_device_model_object && create_device_model_object) { - result = set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + result = set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -246,7 +246,7 @@ namespace nmos } } - control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, create_device_model_object_handler create_device_model_object) + control_protocol_state::control_protocol_state(control_protocol_property_changed_handler property_changed, create_validation_fingerprint_handler create_validation_fingerprint, validate_validation_fingerprint_handler validate_validation_fingerprint, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, create_device_model_object_handler create_device_model_object) { auto to_vector = [](const web::json::value& data) { @@ -384,9 +384,9 @@ namespace nmos to_vector(make_nc_bulk_properties_manager_properties()), to_methods_vector(make_nc_bulk_properties_manager_methods(), { - { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this))}, - { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object) }, - { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object) } + { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), create_validation_fingerprint)}, + { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object) }, + { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object) } }), to_vector(make_nc_bulk_properties_manager_events())) } }; diff --git a/Development/nmos/control_protocol_state.h b/Development/nmos/control_protocol_state.h index 183999e80..6a3c7d0ea 100644 --- a/Development/nmos/control_protocol_state.h +++ b/Development/nmos/control_protocol_state.h @@ -59,7 +59,7 @@ namespace nmos nmos::read_lock read_lock() const { return nmos::read_lock{ mutex }; } nmos::write_lock write_lock() const { return nmos::write_lock{ mutex }; } - control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = nullptr, remove_device_model_object_handler remove_device_model_object = nullptr, create_device_model_object_handler create_device_model_object = nullptr); + control_protocol_state(control_protocol_property_changed_handler property_changed = nullptr, create_validation_fingerprint_handler create_validation_fingerprint = nullptr, validate_validation_fingerprint_handler validate_validation_fingerprintget_read_only_modification_allow_list_handler = nullptr, get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = nullptr, remove_device_model_object_handler remove_device_model_object = nullptr, create_device_model_object_handler create_device_model_object = nullptr); // insert control class descriptor, false if class descriptor already inserted bool insert(const experimental::control_class_descriptor& control_class_descriptor); // erase control class of the given class id, false if the required class not found diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index 725da1b9a..c050f6c48 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -77,7 +77,7 @@ namespace nmos // Configure the Configuration API - node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.get_read_only_modification_allow_list, node_implementation.remove_device_model_object, node_implementation.create_device_model_object, node_implementation.control_protocol_property_changed, gate)); + node_server.api_routers[{ {}, nmos::fields::configuration_port(node_model.settings) }].mount({}, nmos::make_configuration_api(node_model, validate_authorization ? validate_authorization(nmos::experimental::scopes::configuration) : nullptr, node_implementation.get_control_protocol_class_descriptor, node_implementation.get_control_protocol_datatype_descriptor, node_implementation.get_control_protocol_method_descriptor, node_implementation.create_validation_fingerprint, node_implementation.validate_validation_fingerprint, node_implementation.get_read_only_modification_allow_list, node_implementation.remove_device_model_object, node_implementation.create_device_model_object, node_implementation.control_protocol_property_changed, gate)); const auto& events_ws_port = nmos::fields::events_ws_port(node_model.settings); auto& events_ws_api = node_server.ws_handlers[{ {}, nmos::fields::events_ws_port(node_model.settings) }]; diff --git a/Development/nmos/node_server.h b/Development/nmos/node_server.h index 2746c9d38..664953706 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -28,7 +28,7 @@ namespace nmos // underlying implementation into the server instance for the NMOS Node struct node_implementation { - node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, create_device_model_object_handler create_device_model_object) + node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::create_validation_fingerprint_handler create_validation_fingerprint, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, create_device_model_object_handler create_device_model_object) : load_server_certificates(std::move(load_server_certificates)) , load_dh_param(std::move(load_dh_param)) , load_ca_certificates(std::move(load_ca_certificates)) @@ -51,6 +51,8 @@ namespace nmos , get_control_protocol_datatype_descriptor(std::move(get_control_protocol_datatype_descriptor)) , get_control_protocol_method_descriptor(std::move(get_control_protocol_method_descriptor)) , control_protocol_property_changed(std::move(control_protocol_property_changed)) + , create_validation_fingerprint(std::move(create_validation_fingerprint)) + , validate_validation_fingerprint(std::move(validate_validation_fingerprint)) , get_read_only_modification_allow_list(std::move(get_read_only_modification_allow_list)) , remove_device_model_object(std::move(remove_device_model_object)) , create_device_model_object(std::move(create_device_model_object)) @@ -86,6 +88,8 @@ namespace nmos node_implementation& on_get_control_datatype_descriptor(nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { this->get_control_protocol_datatype_descriptor = std::move(get_control_protocol_datatype_descriptor); return *this; } node_implementation& on_get_control_protocol_method_descriptor(nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor) { this->get_control_protocol_method_descriptor = std::move(get_control_protocol_method_descriptor); return *this; } node_implementation& on_control_protocol_property_changed(nmos::control_protocol_property_changed_handler control_protocol_property_changed) { this->control_protocol_property_changed = std::move(control_protocol_property_changed); return *this; } + node_implementation& on_create_validation_fingerprint(nmos::create_validation_fingerprint_handler create_validation_fingerprint) { this->create_validation_fingerprint = std::move(create_validation_fingerprint); return *this; } + node_implementation& on_validate_validation_fingerprint(nmos::validate_validation_fingerprint_handler validate_validation_fingerprint) { this->validate_validation_fingerprint = std::move(validate_validation_fingerprint); return *this; } node_implementation& on_get_read_only_modification_allow_list(nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list) { this->get_read_only_modification_allow_list = std::move(get_read_only_modification_allow_list); return *this; } node_implementation& on_remove_device_model_object(nmos::remove_device_model_object_handler remove_device_model_object) { this->remove_device_model_object = std::move(remove_device_model_object); return *this; } node_implementation& on_create_device_model_object(nmos::create_device_model_object_handler create_device_model_object) { this->create_device_model_object = std::move(create_device_model_object); return *this; } @@ -133,6 +137,8 @@ namespace nmos nmos::control_protocol_property_changed_handler control_protocol_property_changed; // Device Configuration handlers + nmos::create_validation_fingerprint_handler create_validation_fingerprint; + nmos::validate_validation_fingerprint_handler validate_validation_fingerprint; nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list; nmos::remove_device_model_object_handler remove_device_model_object; nmos::create_device_model_object_handler create_device_model_object; diff --git a/Development/nmos/test/configuration_methods_test.cpp b/Development/nmos/test/configuration_methods_test.cpp index 594739a5f..c2a1949a1 100644 --- a/Development/nmos/test/configuration_methods_test.cpp +++ b/Development/nmos/test/configuration_methods_test.cpp @@ -23,6 +23,14 @@ BST_TEST_CASE(testGetPropertiesByPath) nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor = nmos::make_get_control_protocol_class_descriptor_handler(control_protocol_state); nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor = nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state); + bool create_validation_fingerprint_called = false; + // callback stubs + nmos::create_validation_fingerprint_handler create_validation_fingerprint = [&](const nmos::resources& resources, const nmos::resource& resource) + { + create_validation_fingerprint_called = true; + return U("test fingerprint"); + }; + // Create Device Model // root auto root_block = nmos::make_root_block(); @@ -55,9 +63,10 @@ BST_TEST_CASE(testGetPropertiesByPath) insert_resource(resources, std::move(monitor2)); { + create_validation_fingerprint_called = false; const auto target_role_path = value_of({ U("root") }); const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - auto method_result = get_properties_by_path(resources, *resource, true, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + auto method_result = get_properties_by_path(resources, *resource, true, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, create_validation_fingerprint); BST_REQUIRE_EQUAL(nmos::nc_method_status::ok, nmos::fields::nc::status(method_result)); @@ -65,11 +74,14 @@ BST_TEST_CASE(testGetPropertiesByPath) const auto& object_properties_holders = nmos::fields::nc::values(bulk_properties_holder); BST_REQUIRE_EQUAL(5, object_properties_holders.size()); + + BST_CHECK(create_validation_fingerprint_called); } { + create_validation_fingerprint_called = false; const auto target_role_path = value_of({ U("root"), U("receivers") }); const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - auto method_result = get_properties_by_path(resources, *resource, true, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + auto method_result = get_properties_by_path(resources, *resource, true, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, create_validation_fingerprint); BST_REQUIRE_EQUAL(nmos::nc_method_status::ok, nmos::fields::nc::status(method_result)); @@ -77,11 +89,14 @@ BST_TEST_CASE(testGetPropertiesByPath) const auto& object_properties_holders = nmos::fields::nc::values(bulk_properties_holder); BST_REQUIRE_EQUAL(3, object_properties_holders.size()); + + BST_CHECK(create_validation_fingerprint_called); } { + create_validation_fingerprint_called = false; const auto target_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - auto method_result = get_properties_by_path(resources, *resource, true, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor); + auto method_result = get_properties_by_path(resources, *resource, true, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, create_validation_fingerprint); BST_REQUIRE_EQUAL(nmos::nc_method_status::ok, nmos::fields::nc::status(method_result)); @@ -89,5 +104,7 @@ BST_TEST_CASE(testGetPropertiesByPath) const auto& object_properties_holders = nmos::fields::nc::values(bulk_properties_holder); BST_REQUIRE_EQUAL(1, object_properties_holders.size()); + + BST_CHECK(create_validation_fingerprint_called); } } diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 96bc97436..3d20763ac 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -1514,6 +1514,8 @@ BST_TEST_CASE(testModifyRebuildableBlock) const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, U("oid"), U("NcOid"), true, false, false, false, web::json::value::null()); const auto class_id_property_descriptor = nmos::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, U("classId"), U("NcClassId"), true, false, false, false, web::json::value::null()); + + // No class id specified in the objet properties holder for new monitor causes an error { auto monitor_3_oid = 999; // Create Object Properties Holder for Block, with a Property Holder for the block members @@ -1529,17 +1531,17 @@ BST_TEST_CASE(testModifyRebuildableBlock) } const auto block_object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, block_object_properties_holder); - // Create Object Properties Holder for new monitor const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } + // Create Object Properties Holder for new monitor auto monitor3_property_holders = value::array(); const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { - //auto property_holders = value::array(); + // No property holders, including no class id push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } From e189ee7266eb3df1f0a907592e4db738b1a9b31f Mon Sep 17 00:00:00 2001 From: lo-simon Date: Thu, 24 Jul 2025 17:50:30 +0100 Subject: [PATCH 224/250] Remove the unnecessary nmos::resources for the lambda reference, and on the `get_read_only_modification_allow_list_handler` --- .../nmos-cpp-node/node_implementation.cpp | 18 +++++++++--------- Development/nmos/configuration_handlers.h | 2 +- Development/nmos/configuration_utils.cpp | 2 +- .../nmos/test/configuration_utils_test.cpp | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 3f8a78099..19b7da821 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1747,9 +1747,9 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control // IS-14 Device Configuration callback // This function should generate a fingerprint that can be used for subsequent validation. -nmos::create_validation_fingerprint_handler make_create_validation_fingerprint_handler(const nmos::resources& resources, slog::base_gate& gate) +nmos::create_validation_fingerprint_handler make_create_validation_fingerprint_handler(slog::base_gate& gate) { - return [&resources, &gate](const nmos::resources& resources, const nmos::resource& resource) + return [&gate](const nmos::resources& resources, const nmos::resource& resource) { return U("Sony nmos-cpp node"); }; @@ -1757,9 +1757,9 @@ nmos::create_validation_fingerprint_handler make_create_validation_fingerprint_h // IS-14 Device Configuration callback // This function called by a validate or restore and can be used to validate a validation fingerprint. Returning false will fail the validate or restore operation. -nmos::validate_validation_fingerprint_handler make_validate_validation_fingerprint_handler(const nmos::resources& resources, slog::base_gate& gate) +nmos::validate_validation_fingerprint_handler make_validate_validation_fingerprint_handler(slog::base_gate& gate) { - return [&resources, &gate](const nmos::resources& resources, const nmos::resource& resource, const utility::string_t& validation_fingerprint) + return [&gate](const nmos::resources& resources, const nmos::resource& resource, const utility::string_t& validation_fingerprint) { return true; }; @@ -1769,9 +1769,9 @@ nmos::validate_validation_fingerprint_handler make_validate_validation_fingerpri // This function is called when the Device Configuration API is attempting to modify // the read only properties of a rebuildable Device Model object. This callback returns an "allow list" of property ids // for properties that can be updated - the "allowed" read only property will be updated according to backup dataset received. -nmos::get_read_only_modification_allow_list_handler make_get_read_only_modification_allow_list_handler(nmos::resources& resources, slog::base_gate& gate) +nmos::get_read_only_modification_allow_list_handler make_get_read_only_modification_allow_list_handler(slog::base_gate& gate) { - return [&resources, &gate](const nmos::resource& resource, const std::vector& target_role_path, const std::vector& property_ids) + return [&gate](const nmos::resources& resources, const nmos::resource& resource, const std::vector& target_role_path, const std::vector& property_ids) { // Use this function to create allow list of property ids for properties in the object should be modified by the configuration API slog::log(gate, SLOG_FLF) << nmos::stash_category(impl::categories::node_implementation) << "Do filter_property_holders"; @@ -1974,9 +1974,9 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode .on_validate_channelmapping_output_map(make_node_implementation_map_validator()) // may be omitted if not required .on_channelmapping_activated(make_node_implementation_channelmapping_activation_handler(gate)) .on_control_protocol_property_changed(make_node_implementation_control_protocol_property_changed_handler(gate)) // may be omitted if IS-12 not required - .on_create_validation_fingerprint(make_create_validation_fingerprint_handler(model.control_protocol_resources, gate)) - .on_validate_validation_fingerprint(make_validate_validation_fingerprint_handler(model.control_protocol_resources, gate)) - .on_get_read_only_modification_allow_list(make_get_read_only_modification_allow_list_handler(model.control_protocol_resources, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required + .on_create_validation_fingerprint(make_create_validation_fingerprint_handler(gate)) + .on_validate_validation_fingerprint(make_validate_validation_fingerprint_handler(gate)) + .on_get_read_only_modification_allow_list(make_get_read_only_modification_allow_list_handler(gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required .on_remove_device_model_object(make_remove_device_model_object_handler(model, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required .on_create_device_model_object(make_create_device_model_object_handler(model, gate)); // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required } diff --git a/Development/nmos/configuration_handlers.h b/Development/nmos/configuration_handlers.h index 84f5df1b9..186347387 100644 --- a/Development/nmos/configuration_handlers.h +++ b/Development/nmos/configuration_handlers.h @@ -27,7 +27,7 @@ namespace nmos // This callback is invoked if attempting to modify read only properties when restoring a configuration. // This function return a vector of property ids of the read only properties allowed to be modified - typedef std::function(const nmos::resource& resource, const std::vector& role_path, const std::vector& property_ids)> get_read_only_modification_allow_list_handler; + typedef std::function(const nmos::resources& resources, const nmos::resource& resource, const std::vector& role_path, const std::vector& property_ids)> get_read_only_modification_allow_list_handler; // This callback is invoked if attempting to remove a device model object when restoring a configuration. // This function calls back before an object is removed from the device model. diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 40d8aef5b..5aa5562b3 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -115,7 +115,7 @@ namespace nmos } } - const auto allow_list_read_only_property_ids = get_read_only_modification_allow_list(resource, target_role_path_array, read_only_property_ids); + const auto allow_list_read_only_property_ids = get_read_only_modification_allow_list(resources, resource, target_role_path_array, read_only_property_ids); const auto& allowed_property_values = boost::copy_range>(filtered_property_values | boost::adaptors::filtered([&property_restore_notices, get_control_protocol_class_descriptor, class_id, allow_list_read_only_property_ids](const web::json::value& property_value) diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 3d20763ac..7a20150ed 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -380,7 +380,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool create_device_model_object_called = false; // callback stubs - nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resource& resource, const std::vector& target_role_path, const std::vector& property_ids) + nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resources& resources, const nmos::resource& resource, const std::vector& target_role_path, const std::vector& property_ids) { get_read_only_modification_allow_list_called = true; return property_ids; @@ -949,7 +949,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) bool create_device_model_object_called = false; // callback stubs - nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resource& resource, const std::vector& target_role_path, const std::vector& property_ids) + nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resources& resources, const nmos::resource& resource, const std::vector& target_role_path, const std::vector& property_ids) { get_read_only_modification_allow_list_called = true; return property_ids; @@ -1177,7 +1177,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) bool create_device_model_object_called = false; // callback stubs - nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resource& resource, const std::vector& target_role_path, const std::vector& property_ids) + nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resources& resources, const nmos::resource& resource, const std::vector& target_role_path, const std::vector& property_ids) { get_read_only_modification_allow_list_called = true; return property_ids; @@ -1488,7 +1488,7 @@ BST_TEST_CASE(testModifyRebuildableBlock) bool create_device_model_object_called = false; // callback stubs - nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resource& resource, const std::vector& target_role_path, const std::vector& property_ids) + nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list = [&](const nmos::resources& resources, const nmos::resource& resource, const std::vector& target_role_path, const std::vector& property_ids) { get_read_only_modification_allow_list_called = true; return property_ids; From 08c3a40d5256b1b7e15de1e2610e447e3b70bc18 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 25 Jul 2025 09:19:41 +0100 Subject: [PATCH 225/250] Add `configuration_port` settings to node example configuration JSON --- Development/nmos-cpp-node/config.json | 1 + 1 file changed, 1 insertion(+) diff --git a/Development/nmos-cpp-node/config.json b/Development/nmos-cpp-node/config.json index f7e5216e6..77eb203fe 100644 --- a/Development/nmos-cpp-node/config.json +++ b/Development/nmos-cpp-node/config.json @@ -149,6 +149,7 @@ //"system_port": 10641, // control_protocol_ws_port [node]: used to construct request URLs for the Control Protocol websocket, or negative to disable the control protocol features //"control_protocol_ws_port": 3218, + //"configuration_port": 3219, // listen_backlog [registry, node]: the maximum length of the queue of pending connections, or zero for the implementation default (the implementation may not honour this value) //"listen_backlog": 0, From 92324a1663f19d7e5ae67f32288fd154537b9c8a Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 25 Jul 2025 10:34:29 +0100 Subject: [PATCH 226/250] Update Readme to include IS-14 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0f3d6219f..7fa255150 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ This repository contains an implementation of the [AMWA Networked Media Open Spe - [AMWA IS-09 NMOS System Parameters Specification](https://specs.amwa.tv/is-09/) (originally defined in JT-NM TR-1001-1:2018 Annex A) - [AMWA IS-10 NMOS Authorization Specification](https://specs.amwa.tv/is-10/) - [AMWA IS-12 AMWA IS-12 NMOS Control Protocol](https://specs.amwa.tv/is-12/) +- [AMWA IS-14 AMWA IS-14 NMOS Device Configuration Specification](https://specs.amwa.tv/is-14/) - [AMWA BCP-002-01 NMOS Grouping Recommendations - Natural Grouping](https://specs.amwa.tv/bcp-002-01/) - [AMWA BCP-002-02 NMOS Asset Distinguishing Information](https://specs.amwa.tv/bcp-002-02/) - [AMWA BCP-003-01 Secure Communication in NMOS Systems](https://specs.amwa.tv/bcp-003-01/) @@ -127,6 +128,7 @@ The implementation is designed to be extended. Development is ongoing, following Recent activity on the project (newest first): +- Added support for the IS-14 NMOS Device Configuration - Added support for the IS-12 NMOS Control Protocol - Update to Conan 2; Conan 1.X is no longer supported - Added support for IS-10 Authorization From 93b96ec01f15b1ca729b5b6af8d811e664c28007 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 25 Jul 2025 15:42:20 +0100 Subject: [PATCH 227/250] Add IS-14 test suite to front page and CI --- README.md | 3 +++ Sandbox/run_nmos_testing.sh | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/README.md b/README.md index 7fa255150..4d5982e2a 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,7 @@ The [AMWA NMOS API Testing Tool](https://github.com/AMWA-TV/nmos-testing) is aut [![IS-09-01][IS-09-01-badge]][IS-09-01-sheet] [![IS-09-02][IS-09-02-badge]][IS-09-02-sheet] [![IS-12-01][IS-12-01-badge]][IS-12-01-sheet] +[![IS-14-01][IS-14-01-badge]][IS-14-01-sheet] [BCP-003-01-badge]: https://raw.githubusercontent.com/sony/nmos-cpp/badges/BCP-003-01.svg [BCP-006-01-01-badge]: https://raw.githubusercontent.com/sony/nmos-cpp/badges/BCP-006-01-01.svg @@ -107,6 +108,7 @@ The [AMWA NMOS API Testing Tool](https://github.com/AMWA-TV/nmos-testing) is aut [IS-09-01-badge]: https://raw.githubusercontent.com/sony/nmos-cpp/badges/IS-09-01.svg [IS-09-02-badge]: https://raw.githubusercontent.com/sony/nmos-cpp/badges/IS-09-02.svg [IS-12-01-badge]: https://raw.githubusercontent.com/sony/nmos-cpp/badges/IS-12-01.svg +[IS-14-01-badge]: https://raw.githubusercontent.com/sony/nmos-cpp/badges/IS-14-01.svg [BCP-003-01-sheet]: https://docs.google.com/spreadsheets/d/1UgZoI0lGCMDn9-zssccf2Azil3WN6jogroMT8Wh6H64/edit#gid=468090822 [BCP-006-01-01-sheet]: https://docs.google.com/spreadsheets/d/1UgZoI0lGCMDn9-zssccf2Azil3WN6jogroMT8Wh6H64/edit?gid=1835994800 [IS-04-01-sheet]: https://docs.google.com/spreadsheets/d/1UgZoI0lGCMDn9-zssccf2Azil3WN6jogroMT8Wh6H64/edit#gid=0 @@ -121,6 +123,7 @@ The [AMWA NMOS API Testing Tool](https://github.com/AMWA-TV/nmos-testing) is aut [IS-09-01-sheet]: https://docs.google.com/spreadsheets/d/1UgZoI0lGCMDn9-zssccf2Azil3WN6jogroMT8Wh6H64/edit#gid=919453974 [IS-09-02-sheet]: https://docs.google.com/spreadsheets/d/1UgZoI0lGCMDn9-zssccf2Azil3WN6jogroMT8Wh6H64/edit#gid=2135469955 [IS-12-01-sheet]: https://docs.google.com/spreadsheets/d/1UgZoI0lGCMDn9-zssccf2Azil3WN6jogroMT8Wh6H64/edit?gid=1026699230 +[IS-14-01-sheet]: https://docs.google.com/spreadsheets/d/1UgZoI0lGCMDn9-zssccf2Azil3WN6jogroMT8Wh6H64/edit?gid=342707873 ### Recent Activity diff --git a/Sandbox/run_nmos_testing.sh b/Sandbox/run_nmos_testing.sh index 450892b66..be6926732 100755 --- a/Sandbox/run_nmos_testing.sh +++ b/Sandbox/run_nmos_testing.sh @@ -37,6 +37,7 @@ expected_disabled_IS_09_02=0 expected_disabled_IS_04_02=0 expected_disabled_IS_09_01=0 expected_disabled_IS_12_01=3 +expected_disabled_IS_14_01=1 expected_disabled_BCP_006_01_01=0 config_secure=`${run_python} -c $'from nmostesting import Config\nprint(Config.ENABLE_HTTPS)'` || (echo "error running python"; exit 1) @@ -146,6 +147,7 @@ else # test_33, test_33_1 (( expected_disabled_IS_04_02+=16 )) (( expected_disabled_IS_09_01+=7 )) + (( expected_disabled_IS_14_01+=7 )) (( expected_disabled_BCP_006_01_01+=7 )) fi @@ -217,6 +219,8 @@ do_run_test IS-09-02 $expected_disabled_IS_09_02 --host "${host}" null --port 0 do_run_test IS-12-01 $expected_disabled_IS_12_01 --host "${host}" "${host}" null null --port 1080 1082 0 0 --version v1.3 v1.0 v1.0 v1.0 --urlpath null x-nmos/ncp/v1.0 null null --ignore auto_ms05_1.2.2 auto_ms05_NcConnectionStatus test_ms05_05 +do_run_test IS-14_01 $expected_disabled_IS_14_01 --host "${host}" "${host}" null null --port 1080 1080 0 0 --version v1.3 v1.0 v1.0 v1.0 --selector null null null null --ignore test_ms05_05 + do_run_test BCP-006-01-01 $expected_disabled_BCP_006_01_01 --host "${host}" --port 1080 --version v1.3 # Run Registry tests (leave Node running) From 734303339323fd7baa7da17df6fd04b75846009d Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Mon, 28 Jul 2025 08:32:26 +0100 Subject: [PATCH 228/250] Fix IS-14 test suite name in run_nmos_testing --- Sandbox/run_nmos_testing.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sandbox/run_nmos_testing.sh b/Sandbox/run_nmos_testing.sh index be6926732..ddac57556 100755 --- a/Sandbox/run_nmos_testing.sh +++ b/Sandbox/run_nmos_testing.sh @@ -219,7 +219,7 @@ do_run_test IS-09-02 $expected_disabled_IS_09_02 --host "${host}" null --port 0 do_run_test IS-12-01 $expected_disabled_IS_12_01 --host "${host}" "${host}" null null --port 1080 1082 0 0 --version v1.3 v1.0 v1.0 v1.0 --urlpath null x-nmos/ncp/v1.0 null null --ignore auto_ms05_1.2.2 auto_ms05_NcConnectionStatus test_ms05_05 -do_run_test IS-14_01 $expected_disabled_IS_14_01 --host "${host}" "${host}" null null --port 1080 1080 0 0 --version v1.3 v1.0 v1.0 v1.0 --selector null null null null --ignore test_ms05_05 +do_run_test IS-14-01 $expected_disabled_IS_14_01 --host "${host}" "${host}" null null --port 1080 1080 0 0 --version v1.3 v1.0 v1.0 v1.0 --selector null null null null --ignore test_ms05_05 do_run_test BCP-006-01-01 $expected_disabled_BCP_006_01_01 --host "${host}" --port 1080 --version v1.3 From 051ede1fed510266f9061661b974281181df1b98 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Wed, 30 Jul 2025 16:11:30 +0100 Subject: [PATCH 229/250] Add constraint and data type validation when restoring/validating --- Development/nmos/configuration_api.cpp | 12 +++--- Development/nmos/configuration_methods.cpp | 8 ++-- Development/nmos/configuration_methods.h | 4 +- Development/nmos/configuration_utils.cpp | 38 ++++++++++++++----- Development/nmos/configuration_utils.h | 2 +- Development/nmos/control_protocol_state.cpp | 4 +- .../nmos/test/configuration_utils_test.cpp | 38 ++++++++++--------- 7 files changed, 65 insertions(+), 41 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index b98902c93..b8bab3260 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -673,12 +673,12 @@ namespace nmos }); // PATCH /rolePaths/{rolePath}/bulkProperties - invokes validation_set_properties_by_path method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, validate_validation_fingerprint, remove_device_model_object, create_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PATCH, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, validate_validation_fingerprint, remove_device_model_object, create_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, validate_validation_fingerprint, remove_device_model_object, create_device_model_object, version, &gate_](value body) mutable + return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, validate_validation_fingerprint, remove_device_model_object, create_device_model_object, version, &gate_](value body) mutable { auto lock = model.read_lock(); auto& resources = model.control_protocol_resources; @@ -698,7 +698,7 @@ namespace nmos const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); const auto& backup_data_set = nmos::fields::nc::data_set(arguments); - method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); auto status = nmos::fields::nc::status(method_result); if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } @@ -737,12 +737,12 @@ namespace nmos }); // PUT /rolePaths/{rolePath}/bulkProperties - invokes set_properties_by_path method - configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) + configuration_api.support(U("/rolePaths/") + nmos::patterns::rolePath.pattern + U("/bulkProperties/?"), methods::PUT, [&model, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object, &gate_](http_request req, http_response res, const string_t&, const route_parameters& parameters) { const auto role_path = parameters.at(nmos::patterns::rolePath.name); const nmos::api_version version = nmos::parse_api_version(parameters.at(nmos::patterns::version.name)); - return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object, version, &gate_](value body) mutable + return details::extract_json(req, gate_).then([res, &model, role_path, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object, version, &gate_](value body) mutable { auto lock = model.write_lock(); auto& resources = model.control_protocol_resources; @@ -762,7 +762,7 @@ namespace nmos const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); const auto& backup_data_set = nmos::fields::nc::data_set(arguments); - method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); code = status_codes::OK; diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index 3081d7386..a053e3d44 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -100,7 +100,7 @@ namespace nmos return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, bulk_properties_holder); } - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) { if (validate_validation_fingerprint) { @@ -113,12 +113,12 @@ namespace nmos } const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } - web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) { if (validate_validation_fingerprint) { @@ -131,7 +131,7 @@ namespace nmos } const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); - const auto object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } diff --git a/Development/nmos/configuration_methods.h b/Development/nmos/configuration_methods.h index cc351077a..90e9b6531 100644 --- a/Development/nmos/configuration_methods.h +++ b/Development/nmos/configuration_methods.h @@ -17,9 +17,9 @@ namespace nmos // Implementation of IS-14 function for creating backup dataset from a Device Model web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, bool include_descriptors, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::create_validation_fingerprint_handler create_validation_fingerprint); - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); - web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); } #endif diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 5aa5562b3..71f5c9d2d 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -54,7 +54,7 @@ namespace nmos return false; } - web::json::value modify_device_model_object(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& target_object_properties_holder, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list) + web::json::value modify_device_model_object(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& target_object_properties_holder, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list) { const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); @@ -159,19 +159,39 @@ namespace nmos // hmmm, ideally we would pass the value into modify_resource with the validate // flag, so that it's subject to property contraints and also the application code can decide if it's a legal value - if (!validate) + const auto& value = nmos::fields::nc::value(property_value); + try { - // modify control protocol resources - const auto& value = nmos::fields::nc::value(property_value); + nmos::nc::details::constraints_validation(value, nc::details::get_runtime_property_constraints(property_id, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property_descriptor), {nc::details::get_datatype_descriptor(property_descriptor.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor}); - nc::modify_resource(resources, resource.id, [&](nmos::resource& resource_) + if (!validate) + { + // modify control protocol resources + nc::modify_resource(resources, resource.id, [&](nmos::resource& resource_) { resource_.data[nmos::fields::nc::name(property_descriptor)] = value; - }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { {property_id, nmos::nc_property_change_type::type::value_changed, value} })); + }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), {{property_id, nmos::nc_property_change_type::type::value_changed, value}})); + } + } + catch(const nmos::control_protocol_exception& e) + { + // Generate notice for this property + utility::stringstream_t ss; + ss << U("property error: ") << e.what(); + const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::error, ss.str()); + web::json::push_back(property_restore_notices, property_restore_notice); } } - return nmos::make_object_properties_set_validation(target_role_path, nmos::nc_restore_validation_status::ok, property_restore_notices.as_array(), U("OK")); + // If there are any error notices, then give an overall error status for object properties set validation + const auto& error_notices = boost::copy_range>(property_restore_notices.as_array() + | boost::adaptors::filtered([&](const web::json::value& notice) + { + return nmos::fields::nc::notice_type(notice) == nmos::nc_property_restore_notice_type::error; + }) + ); + const auto object_status = error_notices.size() ? nmos::nc_restore_validation_status::failed : nmos::nc_restore_validation_status::ok; + return nmos::make_object_properties_set_validation(target_role_path, object_status, property_restore_notices.as_array(), U("OK")); } web::json::value modify_rebuildable_block(nmos::resources& resources, object_properties_map& object_properties_holder_map, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& block_object_properties_holder, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) @@ -661,7 +681,7 @@ namespace nmos return web::json::value_from_elements(target_object_properties_holders).as_array(); } - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) { auto object_properties_set_validation_values = web::json::value::array(); @@ -749,7 +769,7 @@ namespace nmos } else { - const auto object_properties_set_validation = details::modify_device_model_object(resources, *r, role_path, object_properties_holder, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list); + const auto object_properties_set_validation = details::modify_device_model_object(resources, *r, role_path, object_properties_holder, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list); web::json::push_back(object_properties_set_validation_values, object_properties_set_validation); } diff --git a/Development/nmos/configuration_utils.h b/Development/nmos/configuration_utils.h index 30ee24903..2936ee62b 100644 --- a/Development/nmos/configuration_utils.h +++ b/Development/nmos/configuration_utils.h @@ -27,7 +27,7 @@ namespace nmos // Get object_properties_holder for specified target_role_path web::json::array get_object_properties_holder(const web::json::array& object_properties_holders, const web::json::array& target_role_path); - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); web::json::value get_property_holder(const web::json::value& object_properties_holder, const nmos::nc_property_id& property_id); } diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index d7d5b81b7..dca0ff8b3 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -206,7 +206,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); if (get_read_only_modification_allow_list && remove_device_model_object && create_device_model_object) { - result = validate_set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + result = validate_set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -233,7 +233,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("callbacks not implemented")); if (get_read_only_modification_allow_list && remove_device_model_object && create_device_model_object) { - result = set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + result = set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 7a20150ed..d3d945b52 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -341,6 +341,7 @@ BST_TEST_CASE(testApplyBackupDataSet) nmos::resources resources; nmos::experimental::control_protocol_state control_protocol_state; nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor = nmos::make_get_control_protocol_class_descriptor_handler(control_protocol_state); + nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor= nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state); // Create Device Model // root @@ -423,7 +424,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -463,7 +464,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -503,7 +504,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -555,7 +556,7 @@ BST_TEST_CASE(testApplyBackupDataSet) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -603,7 +604,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -658,7 +659,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors BST_REQUIRE_EQUAL(3, output.as_array().size()); @@ -712,7 +713,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation no object_properties_holders as not in the restore scope BST_REQUIRE_EQUAL(0, output.as_array().size()); @@ -732,6 +733,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) nmos::resources resources; nmos::experimental::control_protocol_state control_protocol_state; nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor = nmos::make_get_control_protocol_class_descriptor_handler(control_protocol_state); + nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor= nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state); // Create Device Model // root @@ -786,7 +788,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -813,7 +815,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -844,7 +846,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -880,7 +882,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holder BST_CHECK_EQUAL(2, output.as_array().size()); @@ -910,6 +912,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) nmos::resources resources; nmos::experimental::control_protocol_state control_protocol_state; nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor = nmos::make_get_control_protocol_class_descriptor_handler(control_protocol_state); + nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor= nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state); // Create Device Model // root @@ -1015,7 +1018,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors BST_REQUIRE_EQUAL(3, output.as_array().size()); @@ -1084,7 +1087,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors BST_REQUIRE_EQUAL(3, output.as_array().size()); @@ -1138,6 +1141,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) nmos::resources resources; nmos::experimental::control_protocol_state control_protocol_state; nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor = nmos::make_get_control_protocol_class_descriptor_handler(control_protocol_state); + nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor= nmos::make_get_control_protocol_datatype_descriptor_handler(control_protocol_state); // Create Device Model // root @@ -1221,7 +1225,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one BST_REQUIRE_EQUAL(1, output.as_array().size()); @@ -1269,7 +1273,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders BST_CHECK_EQUAL(2, output.as_array().size()); @@ -1342,7 +1346,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders i.e. one for the block and one each for the monitors BST_CHECK_EQUAL(4, output.as_array().size()); @@ -1416,7 +1420,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); - const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); // expectation is there will be a result for each of the object_properties_holders BST_CHECK_EQUAL(3, output.as_array().size()); From dfa6bbb52ed2d1ee42022f2a9888abf06338f04f Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 19 Aug 2025 22:12:33 +0100 Subject: [PATCH 230/250] Fix conversion from 'int' to 'web::http::status_code' --- Development/nmos/configuration_api.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index b8bab3260..8ae605dca 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -534,7 +534,7 @@ namespace nmos if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } else if (status / 100 == 4) { code = status_codes::BadRequest; } // 4xx error else if (status / 100 == 5) { code = status_codes::InternalError; } // 5xx error - else { code = status; } + else { code = static_cast(status); } } catch (const nmos::control_protocol_exception& e) { @@ -649,7 +649,7 @@ namespace nmos if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } else if (status / 100 == 4) { code = status_codes::BadRequest; } // 4xx error else if (status / 100 == 5) { code = status_codes::InternalError; } // 5xx error - else { code = status; } + else { code = static_cast(status); } } catch (const nmos::control_protocol_exception& e) { @@ -704,7 +704,7 @@ namespace nmos if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } else if (status / 100 == 4) { code = status_codes::BadRequest; } // 4xx error else if (status / 100 == 5) { code = status_codes::InternalError; } // 5xx error - else { code = status; } + else { code = static_cast(status); } } catch (const nmos::control_protocol_exception& e) { From 7c9700f9daf52341200ccb733b91510d1f0d834e Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 20 Aug 2025 15:29:42 +0100 Subject: [PATCH 231/250] Cast value to the correct web::json::value type for the make_nc_property_holder --- .../nmos/test/configuration_utils_test.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index d3d945b52..187ec0d36 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -451,9 +451,8 @@ BST_TEST_CASE(testApplyBackupDataSet) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); - const nmos::nc_property_id property_id(2, 1); // This is a read only property - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value("change this value")); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value(U("change this value"))); push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); @@ -491,9 +490,8 @@ BST_TEST_CASE(testApplyBackupDataSet) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon2") }); auto property_holders = value::array(); - const nmos::nc_property_id property_id(2, 1); // This is a read only property - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value("change this value")); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value(U("change this value"))); push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); @@ -542,11 +540,10 @@ BST_TEST_CASE(testApplyBackupDataSet) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); - const nmos::nc_property_id property_id(2, 1); // This is a read only property - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value("change this value"))); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value(U("change this value")))); // This is a writable property - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, false)); + push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false))); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths @@ -778,7 +775,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, false); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); @@ -805,7 +802,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, false); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); @@ -832,10 +829,9 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); - const nmos::nc_property_id property_id(2, 1); // This is a read only property const auto property_descriptor = nmos::details::make_nc_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, U("connectionStatusMessage"), U("NcString"), true, false, false, false, web::json::value::null()); - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, property_descriptor, value("change this value")); + const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, property_descriptor, value(U("change this value"))); push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); From 28574a3e16e4721b588a7c7eea653eb5ccdfeb18 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 20 Aug 2025 15:30:25 +0100 Subject: [PATCH 232/250] Add reference comments --- Development/nmos/control_protocol_typedefs.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Development/nmos/control_protocol_typedefs.h b/Development/nmos/control_protocol_typedefs.h index cf1cbdd88..83f30e25f 100644 --- a/Development/nmos/control_protocol_typedefs.h +++ b/Development/nmos/control_protocol_typedefs.h @@ -125,6 +125,7 @@ namespace nmos } // NcRestoreMode + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestoremode namespace nc_restore_mode { enum restore_mode @@ -135,6 +136,7 @@ namespace nmos } // NcRestoreValidationStatus + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestorevalidationstatus namespace nc_restore_validation_status { enum status @@ -147,6 +149,7 @@ namespace nmos } // NcPropertyRestoreNoticeType + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenoticetype namespace nc_property_restore_notice_type { enum type From daa51df797ba18088669ebb123fd792c88ce8410 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 20 Aug 2025 15:36:28 +0100 Subject: [PATCH 233/250] Fix the expected nc_restore_validation_status for the testApplyBackupDataSet test --- Development/nmos/test/configuration_utils_test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 187ec0d36..f57b29c0f 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -510,7 +510,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const auto object_properties_set_validation = output.as_array().at(0); // make sure the validation status propagates from the callback - BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); const auto& property_restore_notices = nmos::fields::nc::notices(object_properties_set_validation); // expectation a single notice for the read only property that couldn't be changed @@ -560,9 +560,9 @@ BST_TEST_CASE(testApplyBackupDataSet) const auto object_properties_set_validation = output.as_array().at(0); - // expect overall status for object to be OK as although the read only property change should fail - // the writable property should succeed - BST_CHECK_EQUAL(nmos::nc_restore_validation_status::ok, nmos::fields::nc::status(object_properties_set_validation)); + // expect overall status for object to be failed as although the writable property should succeed, + // the read only property change should fail with an error notice + BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); const auto& property_restore_notices = nmos::fields::nc::notices(object_properties_set_validation); // expectation a single notice for the read only property that couldn't be changed From e7e4b8cbc63eac1bfc57ac5a0b02b1f649bf8e75 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 20 Aug 2025 18:21:36 +0100 Subject: [PATCH 234/250] Add a helper function to insert root block and all sub resources --- Development/nmos/control_protocol_utils.cpp | 25 +++++++++- Development/nmos/control_protocol_utils.h | 5 +- .../nmos/test/configuration_methods_test.cpp | 8 ++-- .../nmos/test/configuration_utils_test.cpp | 48 +++++++------------ 4 files changed, 48 insertions(+), 38 deletions(-) diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index c75f755e1..eef43ed7d 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -590,7 +590,7 @@ namespace nmos // push a control protocol resource into other control protocol NcBlock resource void push_back(control_protocol_resource& nc_block_resource, const control_protocol_resource& resource) { - // note, model write lock should aleady be applied by the outer function, so access to control_protocol_resources is OK... + // note, model write lock should already be applied by the outer function, so access to control_protocol_resources is OK... using web::json::value; @@ -605,6 +605,27 @@ namespace nmos nc_block_resource.resources.push_back(resource); } + // insert root block and all sub control protocol resources + void insert_root(resources& resources, control_protocol_resource& root) + { + // note, model write lock should already be applied by the outer function, so access to control_protocol_resources is OK... + + std::function insert_resources; + + insert_resources = [&insert_resources](nmos::resources& resources, nmos::control_protocol_resource& resource) + { + for (auto& r : resource.resources) + { + insert_resources(resources, r); + nmos::nc::insert_resource(resources, std::move(r)); + } + resource.resources.clear(); + }; + + insert_resources(resources, root); + nmos::nc::insert_resource(resources, std::move(root)); + } + // insert a control protocol resource std::pair insert_resource(resources& resources, resource&& resource) { @@ -625,7 +646,7 @@ namespace nmos // modify a control protocol resource, and insert notification event to all subscriptions bool modify_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event) { - // note, model write lock should aleady be applied by the outer function, so access to control_protocol_resources is OK... + // note, model write lock should already be applied by the outer function, so access to control_protocol_resources is OK... auto found = resources.find(id); if (resources.end() == found || !found->has_data()) return false; diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index f5bc216e2..d59318506 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -76,6 +76,9 @@ namespace nmos // push control protocol resource into other control protocol NcBlock resource void push_back(control_protocol_resource& nc_block_resource, const control_protocol_resource& resource); + // insert root block and all sub control protocol resources + void insert_root(resources& resources, control_protocol_resource& root); + // insert a control protocol resource std::pair insert_resource(resources& resources, resource&& resource); @@ -85,7 +88,7 @@ namespace nmos // erase a control protocol resource resources::size_type erase_resource(resources& resources, const id& id); - // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id + // find the control protocol resource which is associated with the given IS-04/IS-05/IS-08 resource id resources::const_iterator find_resource(resources& resources, type type, const id& id); // find resource based on role path. diff --git a/Development/nmos/test/configuration_methods_test.cpp b/Development/nmos/test/configuration_methods_test.cpp index c2a1949a1..fbbf2cf8c 100644 --- a/Development/nmos/test/configuration_methods_test.cpp +++ b/Development/nmos/test/configuration_methods_test.cpp @@ -56,11 +56,9 @@ BST_TEST_CASE(testGetPropertiesByPath) nmos::nc::push_back(root_block, receivers); // add class-manager to root-block nmos::nc::push_back(root_block, class_manager); - insert_resource(resources, std::move(root_block)); - insert_resource(resources, std::move(class_manager)); - insert_resource(resources, std::move(receivers)); - insert_resource(resources, std::move(monitor1)); - insert_resource(resources, std::move(monitor2)); + + // insert root block and all sub control protocol resources to resource list + nmos::nc::insert_root(resources, root_block); { create_validation_fingerprint_called = false; diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index f57b29c0f..4f8979e43 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -311,11 +311,9 @@ BST_TEST_CASE(testGetRolePath) nmos::nc::push_back(root_block, receivers); // add class-manager to root-block nmos::nc::push_back(root_block, class_manager); - insert_resource(resources, std::move(root_block)); - insert_resource(resources, std::move(class_manager)); - insert_resource(resources, std::move(receivers)); - insert_resource(resources, std::move(monitor1)); - insert_resource(resources, std::move(monitor2)); + + // insert root block and all sub control protocol resources to resource list + nmos::nc::insert_root(resources, root_block); auto expected_role_paths = value::array(); push_back(expected_role_paths, value_of({ U("root") })); @@ -370,11 +368,9 @@ BST_TEST_CASE(testApplyBackupDataSet) nmos::nc::push_back(root_block, receivers); // add class-manager to root-block nmos::nc::push_back(root_block, class_manager); - insert_resource(resources, std::move(root_block)); - insert_resource(resources, std::move(class_manager)); - insert_resource(resources, std::move(receivers)); - insert_resource(resources, std::move(monitor1)); - insert_resource(resources, std::move(monitor2)); + + // insert root block and all sub control protocol resources to resource list + nmos::nc::insert_root(resources, root_block); bool get_read_only_modification_allow_list_called = false; bool remove_device_model_object_called = false; @@ -756,11 +752,9 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) nmos::nc::push_back(root_block, receivers); // add class-manager to root-block nmos::nc::push_back(root_block, class_manager); - insert_resource(resources, std::move(root_block)); - insert_resource(resources, std::move(class_manager)); - insert_resource(resources, std::move(receivers)); - insert_resource(resources, std::move(monitor1)); - insert_resource(resources, std::move(monitor2)); + + // insert root block and all sub control protocol resources to resource list + nmos::nc::insert_root(resources, root_block); // undefined callback stubs nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list; @@ -937,11 +931,9 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) nmos::nc::push_back(root_block, receivers); // add class-manager to root-block nmos::nc::push_back(root_block, class_manager); - insert_resource(resources, std::move(root_block)); - insert_resource(resources, std::move(class_manager)); - insert_resource(resources, std::move(receivers)); - insert_resource(resources, std::move(monitor1)); - insert_resource(resources, std::move(monitor2)); + + // insert root block and all sub control protocol resources to resource list + nmos::nc::insert_root(resources, root_block); bool get_read_only_modification_allow_list_called = false; bool remove_device_model_object_called = false; @@ -1166,11 +1158,9 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) nmos::nc::push_back(root_block, receivers); // add class-manager to root-block nmos::nc::push_back(root_block, class_manager); - insert_resource(resources, std::move(root_block)); - insert_resource(resources, std::move(class_manager)); - insert_resource(resources, std::move(receivers)); - insert_resource(resources, std::move(monitor1)); - insert_resource(resources, std::move(monitor2)); + + // insert root block and all sub control protocol resources to resource list + nmos::nc::insert_root(resources, root_block); bool get_read_only_modification_allow_list_called = false; bool remove_device_model_object_called = false; @@ -1477,11 +1467,9 @@ BST_TEST_CASE(testModifyRebuildableBlock) nmos::nc::push_back(root_block, receivers); // add class-manager to root-block nmos::nc::push_back(root_block, class_manager); - insert_resource(resources, std::move(root_block)); - insert_resource(resources, std::move(class_manager)); - insert_resource(resources, std::move(receivers)); - insert_resource(resources, std::move(monitor1)); - insert_resource(resources, std::move(monitor2)); + + // insert root block and all sub control protocol resources to resource list + nmos::nc::insert_root(resources, root_block); bool get_read_only_modification_allow_list_called = false; bool remove_device_model_object_called = false; From 2bc97a162bf787c4a8332d8dcd4513135e502cc1 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 20 Aug 2025 19:23:59 +0100 Subject: [PATCH 235/250] Use pre-defined value for the make_nc_property_descriptor name parameter --- Development/nmos/test/configuration_utils_test.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 4f8979e43..84cf24fe7 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -435,7 +435,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(!remove_device_model_object_called); BST_CHECK(!create_device_model_object_called); } - const auto connection_status_property_descriptor = nmos::details::make_nc_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, U("connectionStatusMessage"), U("NcString"), true, false, false, false, web::json::value::null()); + const auto connection_status_property_descriptor = nmos::details::make_nc_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::fields::nc::connection_status_message, U("NcString"), true, false, false, false, web::json::value::null()); { // Check get_read_only_modification_allow_list_handler is called when changing a read only property of rebuildable object in Rebuild mode // @@ -513,8 +513,8 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_REQUIRE_EQUAL(1, property_restore_notices.size()); const auto& notice = *property_restore_notices.begin(); - BST_CHECK_EQUAL(nmos::nc_property_id(3, 2), nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); - BST_CHECK_EQUAL(U("connectionStatusMessage"), nmos::fields::nc::name(notice)); + BST_CHECK_EQUAL(nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); + BST_CHECK_EQUAL(nmos::fields::nc::connection_status_message.key, nmos::fields::nc::name(notice)); BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); @@ -565,8 +565,8 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_REQUIRE_EQUAL(1, property_restore_notices.size()); const auto& notice = *property_restore_notices.begin(); - BST_CHECK_EQUAL(nmos::nc_property_id(3, 2), nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); - BST_CHECK_EQUAL(U("connectionStatusMessage"), nmos::fields::nc::name(notice)); + BST_CHECK_EQUAL(nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); + BST_CHECK_EQUAL(nmos::fields::nc::connection_status_message.key, nmos::fields::nc::name(notice)); BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); @@ -824,7 +824,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); // This is a read only property - const auto property_descriptor = nmos::details::make_nc_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, U("connectionStatusMessage"), U("NcString"), true, false, false, false, web::json::value::null()); + const auto property_descriptor = nmos::details::make_nc_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::fields::nc::connection_status_message, U("NcString"), true, false, false, false, web::json::value::null()); const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, property_descriptor, value(U("change this value"))); push_back(property_holders, property_holder); const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); From f71b162497b1354e001f823163ef48e4fd2d2fa0 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 20 Aug 2025 21:39:35 +0100 Subject: [PATCH 236/250] Use pre-defined value for the make_nc_property_descriptor name parameter --- .../nmos/test/configuration_utils_test.cpp | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 84cf24fe7..2e83fdba1 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -76,7 +76,7 @@ BST_TEST_CASE(testIsBlockModified) push_back(role_path, U("root")); push_back(role_path, U("receivers")); - const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); // Members unchanged { @@ -226,7 +226,7 @@ BST_TEST_CASE(testGetObjectPropertiesHolder) using web::json::value_of; using web::json::value; - const auto enabled_property_descriptor = nmos::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, U("enabled"), U("NcBoolean"), false, false, false, false, web::json::value::null()); + const auto enabled_property_descriptor = nmos::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, web::json::value::null()); // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -401,8 +401,8 @@ BST_TEST_CASE(testApplyBackupDataSet) return nmos::control_protocol_resource({ nmos::is12_versions::v1_0, nmos::types::nc_block, std::move(data), true }); }; - const auto enabled_property_descriptor = nmos::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, U("enabled"), U("NcBoolean"), false, false, false, false, web::json::value::null()); - const auto class_id_property_descriptor = nmos::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, U("classId"), U("NcClassId"), true, false, false, false, web::json::value::null()); + const auto enabled_property_descriptor = nmos::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, web::json::value::null()); + const auto class_id_property_descriptor = nmos::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, web::json::value::null()); { // Check the successful modification of the "enabled" flag of mon1's worker base class in Modify mode // @@ -574,7 +574,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(!remove_device_model_object_called); BST_CHECK(!create_device_model_object_called); } - const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); { // Check remove_device_model_object_called is called when trying to modify a rebuildable block // @@ -611,7 +611,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(remove_device_model_object_called); BST_CHECK(!create_device_model_object_called); } - const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, U("oid"), U("NcOid"), true, false, false, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); { // Check create_device_model_object_called is called when trying to modify a rebuildable block // @@ -761,7 +761,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) nmos::remove_device_model_object_handler remove_device_model_object; nmos::create_device_model_object_handler create_device_model_object; - const auto enabled_property_descriptor = nmos::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, U("enabled"), U("NcBoolean"), false, false, false, false, web::json::value::null()); + const auto enabled_property_descriptor = nmos::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, web::json::value::null()); { // Check that Modify mode is unaffected by undefined Rebuild mode callbacks // @@ -847,8 +847,8 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); } { - const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); - const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, U("oid"), U("NcOid"), true, false, false, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); // Check undefined create_device_model_object and remove_device_model_object causes an unsupported error when attempting to modify a rebuildable block // // Create Object Properties Holder @@ -964,9 +964,9 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) return nmos::control_protocol_resource({ nmos::is12_versions::v1_0, nmos::types::nc_block, std::move(data), true }); }; - const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); - const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, U("oid"), U("NcOid"), true, false, false, false, web::json::value::null()); - const auto class_id_property_descriptor = nmos::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, U("classId"), U("NcClassId"), true, false, false, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); + const auto class_id_property_descriptor = nmos::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, web::json::value::null()); { // Handle constant oid clash // @@ -1187,8 +1187,8 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) // Simulate error on adding object to device model return nmos::control_protocol_resource(); }; - const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); - const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, U("oid"), U("NcOid"), true, false, false, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); { // Check remove_device_model_object_called error is handled when attempting to modify a rebuildable block // @@ -1499,9 +1499,9 @@ BST_TEST_CASE(testModifyRebuildableBlock) return nmos::control_protocol_resource({ nmos::is12_versions::v1_0, nmos::types::nc_block, std::move(data), true }); }; - const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, U("members"), U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); - const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, U("oid"), U("NcOid"), true, false, false, false, web::json::value::null()); - const auto class_id_property_descriptor = nmos::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, U("classId"), U("NcClassId"), true, false, false, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); + const auto class_id_property_descriptor = nmos::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, web::json::value::null()); // No class id specified in the objet properties holder for new monitor causes an error { From 31551c500ac48c9c1d3acce5b01ff7812d73b127 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 20 Aug 2025 22:03:38 +0100 Subject: [PATCH 237/250] Change nmos::nc_restore_mode::restore_mode for restore_mode instead of web::json::value --- Development/nmos/configuration_api.cpp | 4 +-- Development/nmos/configuration_methods.cpp | 4 +-- Development/nmos/configuration_methods.h | 4 +-- Development/nmos/configuration_utils.cpp | 8 ++--- Development/nmos/configuration_utils.h | 2 +- Development/nmos/control_protocol_state.cpp | 4 +-- .../nmos/test/configuration_utils_test.cpp | 34 +++++++++---------- 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 8ae605dca..8e4d3034f 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -698,7 +698,7 @@ namespace nmos const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); const auto& backup_data_set = nmos::fields::nc::data_set(arguments); - method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + method_result = validate_set_properties_by_path(resources, *resource, backup_data_set, recurse, static_cast(restore_mode), get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); auto status = nmos::fields::nc::status(method_result); if (nc_method_status::ok == status || nc_method_status::method_deprecated == status) { code = status_codes::OK; } @@ -762,7 +762,7 @@ namespace nmos const auto& restore_mode = nmos::fields::nc::restore_mode(arguments); const auto& backup_data_set = nmos::fields::nc::data_set(arguments); - method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + method_result = set_properties_by_path(resources, *resource, backup_data_set, recurse, static_cast(restore_mode), get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); code = status_codes::OK; diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index a053e3d44..e16e34341 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -100,7 +100,7 @@ namespace nmos return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, bulk_properties_holder); } - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, nmos::nc_restore_mode::restore_mode restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) { if (validate_validation_fingerprint) { @@ -118,7 +118,7 @@ namespace nmos return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } - web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, nmos::nc_restore_mode::restore_mode restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) { if (validate_validation_fingerprint) { diff --git a/Development/nmos/configuration_methods.h b/Development/nmos/configuration_methods.h index 90e9b6531..46df83cda 100644 --- a/Development/nmos/configuration_methods.h +++ b/Development/nmos/configuration_methods.h @@ -17,9 +17,9 @@ namespace nmos // Implementation of IS-14 function for creating backup dataset from a Device Model web::json::value get_properties_by_path(const nmos::resources& resources, const nmos::resource& resource, bool recurse, bool include_descriptors, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::create_validation_fingerprint_handler create_validation_fingerprint); - web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); + web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, nmos::nc_restore_mode::restore_mode restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); - web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, const web::json::value& restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); + web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, nmos::nc_restore_mode::restore_mode restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); } #endif diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 71f5c9d2d..12bac2b7e 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -15,7 +15,7 @@ namespace nmos { namespace details { - bool is_property_value_valid(web::json::value& property_restore_notices, const web::json::value& property_value, const web::json::value& property_descriptor, const web::json::value& restore_mode, bool is_rebuildable) + bool is_property_value_valid(web::json::value& property_restore_notices, const web::json::value& property_value, const web::json::value& property_descriptor, nmos::nc_restore_mode::restore_mode restore_mode, bool is_rebuildable) { const nmos::nc_property_id& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)); bool is_valid = true; @@ -54,7 +54,7 @@ namespace nmos return false; } - web::json::value modify_device_model_object(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& target_object_properties_holder, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list) + web::json::value modify_device_model_object(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& target_object_properties_holder, nmos::nc_restore_mode::restore_mode restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list) { const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); @@ -76,7 +76,7 @@ namespace nmos ); auto property_modify_list = web::json::value_from_elements(filtered_property_values).as_array(); - if (nmos::nc_restore_mode::rebuild == restore_mode.as_integer()) + if (nmos::nc_restore_mode::rebuild == restore_mode) { // Find any read only properties const auto& read_only_property_values = boost::copy_range>(filtered_property_values @@ -681,7 +681,7 @@ namespace nmos return web::json::value_from_elements(target_object_properties_holders).as_array(); } - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, nmos::nc_restore_mode::restore_mode restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) { auto object_properties_set_validation_values = web::json::value::array(); diff --git a/Development/nmos/configuration_utils.h b/Development/nmos/configuration_utils.h index 2936ee62b..8157d4173 100644 --- a/Development/nmos/configuration_utils.h +++ b/Development/nmos/configuration_utils.h @@ -27,7 +27,7 @@ namespace nmos // Get object_properties_holder for specified target_role_path web::json::array get_object_properties_holder(const web::json::array& object_properties_holders, const web::json::array& target_role_path); - web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, const web::json::value& restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); + web::json::value apply_backup_data_set(nmos::resources& resources, const nmos::resource& resource, const web::json::array& object_properties_holders, bool recurse, nmos::nc_restore_mode::restore_mode restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object); web::json::value get_property_holder(const web::json::value& object_properties_holder, const nmos::nc_property_id& property_id); } diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index dca0ff8b3..8757bad1a 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -206,7 +206,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); if (get_read_only_modification_allow_list && remove_device_model_object && create_device_model_object) { - result = validate_set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + result = validate_set_properties_by_path(resources, resource, data_set, recurse, static_cast(restore_mode), get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) @@ -233,7 +233,7 @@ namespace nmos auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("callbacks not implemented")); if (get_read_only_modification_allow_list && remove_device_model_object && create_device_model_object) { - result = set_properties_by_path(resources, resource, data_set, recurse, restore_mode, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); + result = set_properties_by_path(resources, resource, data_set, recurse, static_cast(restore_mode), get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 2e83fdba1..c0ece72e8 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -417,7 +417,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const auto target_role_path = value_of({ U("root"), U("receivers")}); bool recurse = true; bool validate = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; + const auto restore_mode = nmos::nc_restore_mode::restore_mode::modify; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); @@ -455,7 +455,7 @@ BST_TEST_CASE(testApplyBackupDataSet) // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + const auto restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); @@ -494,7 +494,7 @@ BST_TEST_CASE(testApplyBackupDataSet) // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + const auto restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); @@ -545,7 +545,7 @@ BST_TEST_CASE(testApplyBackupDataSet) // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; + const auto restore_mode = nmos::nc_restore_mode::restore_mode::modify; bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); @@ -594,7 +594,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + const auto restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); @@ -649,7 +649,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + const auto restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); @@ -703,7 +703,7 @@ BST_TEST_CASE(testApplyBackupDataSet) const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + const auto restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); @@ -776,7 +776,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::modify }; + const auto restore_mode = nmos::nc_restore_mode::restore_mode::modify; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); @@ -803,7 +803,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + const auto restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); @@ -832,7 +832,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + const auto restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; bool validate = true; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); @@ -869,7 +869,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + const auto restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); @@ -1003,7 +1003,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + const auto restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); @@ -1072,7 +1072,7 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + const auto restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); @@ -1208,7 +1208,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + const auto restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); @@ -1256,7 +1256,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + const auto restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); @@ -1329,7 +1329,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + const auto restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); @@ -1403,7 +1403,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; - const value restore_mode{ nmos::nc_restore_mode::restore_mode::rebuild }; + const auto restore_mode = nmos::nc_restore_mode::restore_mode::rebuild; const auto& resource = nmos::nc::find_resource_by_role_path(resources, target_role_path.as_array()); const auto output = nmos::apply_backup_data_set(resources, *resource, object_properties_holders.as_array(), recurse, restore_mode, validate, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); From c02c6e26650eeb793a95227953b55cd8bbbdd9fd Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Thu, 11 Sep 2025 15:05:42 +0100 Subject: [PATCH 238/250] Make example control rebuildable rather than the Device Manager --- Development/nmos-cpp-node/node_implementation.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 19b7da821..b168541b6 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1208,8 +1208,6 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // example device manager auto device_manager = nmos::make_device_manager(++oid, model.settings); - // making an object rebuildable allows read only properties to be modified by the Configuration API in Rebuild mode - nmos::make_rebuildable(device_manager); // example class manager auto class_manager = nmos::make_class_manager(++oid, control_protocol_state); @@ -1263,6 +1261,9 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr { make_example_datatype(example_enum::Alpha, U("example"), 50, false), make_example_datatype(example_enum::Gamma, U("different"), 75, true) } ); + // making an object rebuildable allows read only properties to be modified by the Configuration API in Rebuild mode + nmos::make_rebuildable(example_control); + const auto receiver_block_oid = ++oid; auto receiver_block = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receiver Monitors"), U("Receiver Monitors")); // making a block rebuildable allows block members to be added or removed by the Configuration API in Rebuild mode From 8a0ebbea50c2efadd37b03cd7241e18faf54ff01 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 12 Sep 2025 10:25:24 +0100 Subject: [PATCH 239/250] Update README's recent activity --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 924609f9b..25967f0e8 100644 --- a/README.md +++ b/README.md @@ -139,8 +139,10 @@ The implementation is designed to be extended. Development is ongoing, following Recent activity on the project (newest first): -- Added support for the IS-14 NMOS Device Configuration -- Added support for the IS-12 NMOS Control Protocol +- 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 +- Added support for IS-12 NMOS Control Protocol - Update to Conan 2; Conan 1.X is no longer supported - Added support for IS-10 Authorization - Added support for HSTS and OCSP stapling From 79179666cc42328c74382c9ceba2503530cffbdf Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 12 Sep 2025 10:26:02 +0100 Subject: [PATCH 240/250] Move control_protocol_utils functions into nc namespace --- .../nmos/control_protocol_behaviour.cpp | 12 +- .../nmos/control_protocol_handlers.cpp | 28 +- Development/nmos/control_protocol_utils.cpp | 769 +++++++++--------- Development/nmos/control_protocol_utils.h | 98 +-- .../nmos/test/control_protocol_utils_test.cpp | 496 +++++------ 5 files changed, 702 insertions(+), 701 deletions(-) diff --git a/Development/nmos/control_protocol_behaviour.cpp b/Development/nmos/control_protocol_behaviour.cpp index e9f714012..b31eea8da 100644 --- a/Development/nmos/control_protocol_behaviour.cpp +++ b/Development/nmos/control_protocol_behaviour.cpp @@ -89,13 +89,13 @@ namespace nmos auto oid = nmos::fields::nc::oid(descriptor); const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); - auto status_reporting_delay = get_control_protocol_property(control_protocol_resources, oid, nc_status_monitor_status_reporting_delay, get_control_protocol_class_descriptor, gate); + auto status_reporting_delay = nc::get_control_protocol_property(control_protocol_resources, oid, nc_status_monitor_status_reporting_delay, get_control_protocol_class_descriptor, gate); const auto& domain_statuses = nmos::nc::is_nc_sender_monitor(class_id) ? sender_monitor_domain_statuses : receiver_monitor_domain_statuses; for (const auto& domain_status : domain_statuses) { - auto received_time = get_control_protocol_property(control_protocol_resources, oid, domain_status.status_pending_received_time_field_name, gate); + auto received_time = nc::get_control_protocol_property(control_protocol_resources, oid, domain_status.status_pending_received_time_field_name, gate); if (received_time.as_integer() > 0) { @@ -126,13 +126,13 @@ namespace nmos const auto& oid = nmos::fields::nc::oid(descriptor); const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); - const auto status_reporting_delay = get_control_protocol_property(control_protocol_resources, oid, nc_status_monitor_status_reporting_delay, get_control_protocol_class_descriptor, gate); + const auto status_reporting_delay = nc::get_control_protocol_property(control_protocol_resources, oid, nc_status_monitor_status_reporting_delay, get_control_protocol_class_descriptor, gate); const auto& domain_statuses = nmos::nc::is_nc_sender_monitor(class_id) ? sender_monitor_domain_statuses : receiver_monitor_domain_statuses; for (const auto& domain_status : domain_statuses) { - const auto received_time = get_control_protocol_property(control_protocol_resources, oid, domain_status.status_pending_received_time_field_name, gate); + const auto received_time = nc::get_control_protocol_property(control_protocol_resources, oid, domain_status.status_pending_received_time_field_name, gate); if (received_time.as_integer() > 0) { @@ -141,8 +141,8 @@ namespace nmos if (current_time >= threshold_time) { // copy pending status to status property - const auto& status = get_control_protocol_property(control_protocol_resources, oid, domain_status.status_pending_field_name, gate); - const auto& status_message = get_control_protocol_property(control_protocol_resources, oid, domain_status.status_message_pending_field_name, gate); + const auto& status = nc::get_control_protocol_property(control_protocol_resources, oid, domain_status.status_pending_field_name, gate); + const auto& status_message = nc::get_control_protocol_property(control_protocol_resources, oid, domain_status.status_message_pending_field_name, gate); const auto& status_message_string = status_message == web::json::value::null() ? U("") : status_message.as_string(); nc::details::set_monitor_status(control_protocol_resources, oid, status.as_integer(), status_message_string, domain_status.status_property_id, diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index 9e4c0c10f..bee73ed61 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -99,12 +99,12 @@ namespace nmos if (active) { // Activate monitor - activate_monitor(resources, oid, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, gate); + nc::activate_monitor(resources, oid, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, gate); } else { // deactivate monitor - deactivate_monitor(resources, oid, get_control_protocol_class_descriptor, gate); + nc::deactivate_monitor(resources, oid, get_control_protocol_class_descriptor, gate); } } }; @@ -116,7 +116,7 @@ namespace nmos return [&resources, get_control_protocol_class_descriptor, &gate](nc_oid oid, const nc_property_id& property_id) { - return get_control_protocol_property(resources, oid, property_id, get_control_protocol_class_descriptor, gate); + return nc::get_control_protocol_property(resources, oid, property_id, get_control_protocol_class_descriptor, gate); }; } @@ -126,7 +126,7 @@ namespace nmos return [&resources, get_control_protocol_class_descriptor, &gate](nc_oid oid, const nc_property_id& property_id, const web::json::value& value) { - return set_control_protocol_property_and_notify(resources, oid, property_id, value, get_control_protocol_class_descriptor, gate); + return nc::set_control_protocol_property_and_notify(resources, oid, property_id, value, get_control_protocol_class_descriptor, gate); }; } @@ -137,7 +137,7 @@ namespace nmos return [&resources, monitor_status_pending, get_control_protocol_class_descriptor, &gate](nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message) { - return set_receiver_monitor_link_status_with_delay(resources, oid, link_status, link_status_message, monitor_status_pending, get_control_protocol_class_descriptor, gate); + return nc::set_receiver_monitor_link_status_with_delay(resources, oid, link_status, link_status_message, monitor_status_pending, get_control_protocol_class_descriptor, gate); }; } @@ -149,7 +149,7 @@ namespace nmos return [&resources, monitor_status_pending, get_control_protocol_class_descriptor, &gate](nc_oid oid, nmos::nc_connection_status::status connection_status, const utility::string_t& connection_status_message) { - return set_receiver_monitor_connection_status_with_delay(resources, oid, connection_status, connection_status_message, monitor_status_pending, get_control_protocol_class_descriptor, gate); + return nc::set_receiver_monitor_connection_status_with_delay(resources, oid, connection_status, connection_status_message, monitor_status_pending, get_control_protocol_class_descriptor, gate); }; } @@ -161,7 +161,7 @@ namespace nmos return [&resources, monitor_status_pending, get_control_protocol_class_descriptor, &gate](nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message) { - return set_receiver_monitor_external_synchronization_status_with_delay(resources, oid, external_synchronization_status, external_synchronization_status_message, monitor_status_pending, get_control_protocol_class_descriptor, gate); + return nc::set_receiver_monitor_external_synchronization_status_with_delay(resources, oid, external_synchronization_status, external_synchronization_status_message, monitor_status_pending, get_control_protocol_class_descriptor, gate); }; } @@ -173,7 +173,7 @@ namespace nmos return [&resources, monitor_status_pending, get_control_protocol_class_descriptor, &gate](nc_oid oid, nmos::nc_stream_status::status stream_status, const utility::string_t& stream_status_message) { - return set_receiver_monitor_stream_status_with_delay(resources, oid, stream_status, stream_status_message, monitor_status_pending, get_control_protocol_class_descriptor, gate); + return nc::set_receiver_monitor_stream_status_with_delay(resources, oid, stream_status, stream_status_message, monitor_status_pending, get_control_protocol_class_descriptor, gate); }; } @@ -184,7 +184,7 @@ namespace nmos return [&resources, get_control_protocol_class_descriptor, &gate](nc_oid oid, const bst::optional& source_id) { - return set_monitor_synchronization_source_id(resources, oid, source_id, get_control_protocol_class_descriptor, gate); + return nc::set_monitor_synchronization_source_id(resources, oid, source_id, get_control_protocol_class_descriptor, gate); }; } @@ -196,7 +196,7 @@ namespace nmos return [&resources, monitor_status_pending, get_control_protocol_class_descriptor, &gate](nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message) { - return set_sender_monitor_link_status_with_delay(resources, oid, link_status, link_status_message, monitor_status_pending, get_control_protocol_class_descriptor, gate); + return nc::set_sender_monitor_link_status_with_delay(resources, oid, link_status, link_status_message, monitor_status_pending, get_control_protocol_class_descriptor, gate); }; } @@ -208,7 +208,7 @@ namespace nmos return [&resources, monitor_status_pending, get_control_protocol_class_descriptor, &gate](nc_oid oid, nmos::nc_transmission_status::status transmission_status, const utility::string_t& transmission_status_message) { - return set_sender_monitor_transmission_status_with_delay(resources, oid, transmission_status, transmission_status_message, monitor_status_pending, get_control_protocol_class_descriptor, gate); + return nc::set_sender_monitor_transmission_status_with_delay(resources, oid, transmission_status, transmission_status_message, monitor_status_pending, get_control_protocol_class_descriptor, gate); }; } @@ -220,7 +220,7 @@ namespace nmos return [&resources, monitor_status_pending, get_control_protocol_class_descriptor, &gate](nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message) { - return set_sender_monitor_external_synchronization_status_with_delay(resources, oid, external_synchronization_status, external_synchronization_status_message, monitor_status_pending, get_control_protocol_class_descriptor, gate); + return nc::set_sender_monitor_external_synchronization_status_with_delay(resources, oid, external_synchronization_status, external_synchronization_status_message, monitor_status_pending, get_control_protocol_class_descriptor, gate); }; } @@ -232,7 +232,7 @@ namespace nmos return [&resources, monitor_status_pending, get_control_protocol_class_descriptor, &gate](nc_oid oid, nmos::nc_essence_status::status essence_status, const utility::string_t& essence_status_message) { - return set_sender_monitor_essence_status_with_delay(resources, oid, essence_status, essence_status_message, monitor_status_pending, get_control_protocol_class_descriptor, gate); + return nc::set_sender_monitor_essence_status_with_delay(resources, oid, essence_status, essence_status_message, monitor_status_pending, get_control_protocol_class_descriptor, gate); }; } @@ -243,7 +243,7 @@ namespace nmos return [&resources, get_control_protocol_class_descriptor, &gate](nc_oid oid, const bst::optional& source_id) { - return set_monitor_synchronization_source_id(resources, oid, source_id, get_control_protocol_class_descriptor, gate); + return nc::set_monitor_synchronization_source_id(resources, oid, source_id, get_control_protocol_class_descriptor, gate); }; } } diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 2912a66b7..546687e48 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -1044,463 +1044,464 @@ namespace nmos const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); return nmos::find_resource(resources, touchpoint_uuid.as_string()); } - } - - // insert 'value changed', 'sequence item added', 'sequence item changed' or 'sequence item removed' notification events into all grains whose subscriptions match the specified version, type and "pre" or "post" values - // this is used for the IS-12 propertry changed event - void insert_notification_events(nmos::resources& resources, const nmos::api_version& version, const nmos::api_version& downgrade_version, const nmos::type& type, const web::json::value& pre, const web::json::value& post, const web::json::value& event) - { - using web::json::value; - if (pre == post) return; + // insert 'value changed', 'sequence item added', 'sequence item changed' or 'sequence item removed' notification events into all grains whose subscriptions match the specified version, type and "pre" or "post" values + // this is used for the IS-12 propertry changed event + void insert_notification_events(nmos::resources& resources, const nmos::api_version& version, const nmos::api_version& downgrade_version, const nmos::type& type, const web::json::value& pre, const web::json::value& post, const web::json::value& event) + { + using web::json::value; - auto& by_type = resources.get(); - const auto subscriptions = by_type.equal_range(details::has_data(nmos::types::subscription)); + if (pre == post) return; - for (auto it = subscriptions.first; subscriptions.second != it; ++it) - { - // for each subscription - const auto& subscription = *it; + auto& by_type = resources.get(); + const auto subscriptions = by_type.equal_range(nmos::details::has_data(nmos::types::subscription)); - // check whether the resource_path matches the resource type and the query parameters match either the "pre" or "post" resource + for (auto it = subscriptions.first; subscriptions.second != it; ++it) + { + // for each subscription + const auto& subscription = *it; - const auto resource_path = nmos::fields::resource_path(subscription.data); - const resource_query match(subscription.version, resource_path, nmos::fields::params(subscription.data)); + // check whether the resource_path matches the resource type and the query parameters match either the "pre" or "post" resource - const bool pre_match = match(version, downgrade_version, type, pre, resources); - const bool post_match = match(version, downgrade_version, type, post, resources); + const auto resource_path = nmos::fields::resource_path(subscription.data); + const resource_query match(subscription.version, resource_path, nmos::fields::params(subscription.data)); - if (!pre_match && !post_match) continue; + const bool pre_match = match(version, downgrade_version, type, pre, resources); + const bool post_match = match(version, downgrade_version, type, post, resources); - // add the event to the grain for each websocket connection to this subscription + if (!pre_match && !post_match) continue; - for (const auto& id : subscription.sub_resources) - { - auto grain = find_resource(resources, { id, nmos::types::grain }); - if (resources.end() == grain) continue; // check websocket connection is still open + // add the event to the grain for each websocket connection to this subscription - resources.modify(grain, [&resources, &event](nmos::resource& grain) + for (const auto& id : subscription.sub_resources) { - auto& events = nmos::fields::message_grain_data(grain.data); - web::json::push_back(events, event); - grain.updated = strictly_increasing_update(resources); - }); + auto grain = find_resource(resources, { id, nmos::types::grain }); + if (resources.end() == grain) continue; // check websocket connection is still open + + resources.modify(grain, [&resources, &event](nmos::resource& grain) + { + auto& events = nmos::fields::message_grain_data(grain.data); + web::json::push_back(events, event); + grain.updated = strictly_increasing_update(resources); + }); + } } } - } - web::json::value get_control_protocol_property(const resources& resources, nc_oid oid, const nc_property_id& property_id, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - // get resource based on the oid - const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found) + web::json::value get_control_protocol_property(const resources& resources, nc_oid oid, const nc_property_id& property_id, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { - // find the relevant nc_property_descriptor - const auto& property = nc::find_property_descriptor(property_id, details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)), get_control_protocol_class_descriptor); - if (!property.is_null() && found->has_data() && found->data.has_field(nmos::fields::nc::name(property))) + // get resource based on the oid + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) { - return found->data.at(nmos::fields::nc::name(property)); + // find the relevant nc_property_descriptor + const auto& property = nc::find_property_descriptor(property_id, nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)), get_control_protocol_class_descriptor); + if (!property.is_null() && found->has_data() && found->data.has_field(nmos::fields::nc::name(property))) + { + return found->data.at(nmos::fields::nc::name(property)); + } } + // unknown property + slog::log(gate, SLOG_FLF) << U("unknown property: {level=") << property_id.level << U(", index=") << property_id.index << U("} to do Get"); + return web::json::value::null(); } - // unknown property - slog::log(gate, SLOG_FLF) << U("unknown property: {level=") << property_id.level << U(", index=") << property_id.index << U("} to do Get"); - return web::json::value::null(); - } - bool set_control_protocol_property_and_notify(resources& resources, nc_oid oid, const nc_property_id& property_id, const web::json::value& value, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found) + bool set_control_protocol_property_and_notify(resources& resources, nc_oid oid, const nc_property_id& property_id, const web::json::value& value, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { - const auto& property = nc::find_property_descriptor(property_id, details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)), get_control_protocol_class_descriptor); - if (!property.is_null()) + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) { - try + const auto& property = nc::find_property_descriptor(property_id, nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)), get_control_protocol_class_descriptor); + if (!property.is_null()) { - // update property - nc::modify_resource(resources, found->id, [&](nmos::resource& resource) + try { - resource.data[nmos::fields::nc::name(property)] = value; + // update property + nc::modify_resource(resources, found->id, [&](nmos::resource& resource) + { + resource.data[nmos::fields::nc::name(property)] = value; - }, make_property_changed_event(oid, { { property_id, nc_property_change_type::type::value_changed, value } })); + }, make_property_changed_event(oid, { { property_id, nc_property_change_type::type::value_changed, value } })); - return true; - } - catch (const nmos::control_protocol_exception& e) - { - slog::log(gate, SLOG_FLF) << "Set property: {level=" << property_id.level << ", index=" << property_id.index << "} error: " << e.what(); - return false; + return true; + } + catch (const nmos::control_protocol_exception& e) + { + slog::log(gate, SLOG_FLF) << "Set property: {level=" << property_id.level << ", index=" << property_id.index << "} error: " << e.what(); + return false; + } } + + // unknown property + slog::log(gate, SLOG_FLF) << "unknown property: {level=" << property_id.level << ", index=" << property_id.index << "} to do Set."; + return false; } - // unknown property - slog::log(gate, SLOG_FLF) << "unknown property: {level=" << property_id.level << ", index=" << property_id.index << "} to do Set."; + // unknown resource + slog::log(gate, SLOG_FLF) << "unknown control protocol resource: oid=" << oid; return false; } - // unknown resource - slog::log(gate, SLOG_FLF) << "unknown control protocol resource: oid=" << oid; - return false; - } + bool set_control_protocol_property(resources& resources, nc_oid oid, const utility::string_t& property_name, const web::json::value& value, slog::base_gate& gate) + { + try + { + nc::modify_resource(resources, utility::s2us(std::to_string(oid)), [&](nmos::resource& resource) + { + resource.data[property_name] = value; + }); + return true; + } + catch (const nmos::control_protocol_exception& e) + { + slog::log(gate, SLOG_FLF) << "Set property name : " << property_name.c_str() << " error: " << e.what(); + return false; + } + } - bool set_control_protocol_property(resources& resources, nc_oid oid, const utility::string_t& property_name, const web::json::value& value, slog::base_gate& gate) - { - try + web::json::value get_control_protocol_property(const resources& resources, nc_oid oid, const utility::string_t& property_name, slog::base_gate& gate) { - nc::modify_resource(resources, utility::s2us(std::to_string(oid)), [&](nmos::resource& resource) + // get resource based on the oid + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found) { - resource.data[property_name] = value; - }); - return true; + // find the relevant nc_property_descriptor + return found->data.at(property_name); + } + // unknown resource + slog::log(gate, SLOG_FLF) << "unknown control protocol resource: oid=" << oid; + return web::json::value::null(); } - catch (const nmos::control_protocol_exception& e) + + // Set link status and link status message + bool set_receiver_monitor_link_status(resources& resources, nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { - slog::log(gate, SLOG_FLF) << "Set property name : " << property_name.c_str() << " error: " << e.what(); - return false; + return nc::details::set_monitor_status(resources, oid, link_status, link_status_message, + nc_receiver_monitor_link_status_property_id, + nc_receiver_monitor_link_status_message_property_id, + nc_receiver_monitor_link_status_transition_counter_property_id, + nmos::fields::nc::link_status_pending_received_time, + get_control_protocol_class_descriptor, + gate); } - } - web::json::value get_control_protocol_property(const resources& resources, nc_oid oid, const utility::string_t& property_name, slog::base_gate& gate) - { - // get resource based on the oid - const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found) + bool set_receiver_monitor_link_status_with_delay(resources& resources, nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { - // find the relevant nc_property_descriptor - return found->data.at(property_name); + const auto now_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + + return nc::details::set_monitor_status_with_delay(resources, oid, link_status, link_status_message, + nc_receiver_monitor_link_status_property_id, + nc_receiver_monitor_link_status_message_property_id, + nc_receiver_monitor_link_status_transition_counter_property_id, + nmos::fields::nc::link_status_pending, + nmos::fields::nc::link_status_message_pending, + nmos::fields::nc::link_status_pending_received_time, + now_time, + monitor_status_pending, + get_control_protocol_class_descriptor, + gate); } - // unknown resource - slog::log(gate, SLOG_FLF) << "unknown control protocol resource: oid=" << oid; - return web::json::value::null(); - } - // Set link status and link status message - bool set_receiver_monitor_link_status(resources& resources, nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - return nc::details::set_monitor_status(resources, oid, link_status, link_status_message, - nc_receiver_monitor_link_status_property_id, - nc_receiver_monitor_link_status_message_property_id, - nc_receiver_monitor_link_status_transition_counter_property_id, - nmos::fields::nc::link_status_pending_received_time, - get_control_protocol_class_descriptor, - gate); - } + bool set_receiver_monitor_connection_status(resources& resources, nc_oid oid, nmos::nc_connection_status::status connection_status, const utility::string_t& connection_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + { + return nc::details::set_monitor_status(resources, oid, connection_status, connection_status_message, + nc_receiver_monitor_connection_status_property_id, + nc_receiver_monitor_connection_status_message_property_id, + nc_receiver_monitor_connection_status_transition_counter_property_id, + nmos::fields::nc::connection_status_pending_received_time, + get_control_protocol_class_descriptor, + gate); + } - bool set_receiver_monitor_link_status_with_delay(resources& resources, nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - const auto now_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - - return nc::details::set_monitor_status_with_delay(resources, oid, link_status, link_status_message, - nc_receiver_monitor_link_status_property_id, - nc_receiver_monitor_link_status_message_property_id, - nc_receiver_monitor_link_status_transition_counter_property_id, - nmos::fields::nc::link_status_pending, - nmos::fields::nc::link_status_message_pending, - nmos::fields::nc::link_status_pending_received_time, - now_time, - monitor_status_pending, - get_control_protocol_class_descriptor, - gate); - } + bool set_receiver_monitor_connection_status_with_delay(resources& resources, nc_oid oid, nmos::nc_connection_status::status connection_status, const utility::string_t& connection_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + { + const auto now_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + + return nc::details::set_monitor_status_with_delay(resources, oid, connection_status, connection_status_message, + nc_receiver_monitor_connection_status_property_id, + nc_receiver_monitor_connection_status_message_property_id, + nc_receiver_monitor_connection_status_transition_counter_property_id, + nmos::fields::nc::connection_status_pending, + nmos::fields::nc::connection_status_message_pending, + nmos::fields::nc::connection_status_pending_received_time, + now_time, + monitor_status_pending, + get_control_protocol_class_descriptor, + gate); + } - bool set_receiver_monitor_connection_status(resources& resources, nc_oid oid, nmos::nc_connection_status::status connection_status, const utility::string_t& connection_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - return nc::details::set_monitor_status(resources, oid, connection_status, connection_status_message, - nc_receiver_monitor_connection_status_property_id, - nc_receiver_monitor_connection_status_message_property_id, - nc_receiver_monitor_connection_status_transition_counter_property_id, - nmos::fields::nc::connection_status_pending_received_time, - get_control_protocol_class_descriptor, - gate); - } + // Set external synchronization status and external synchronization status message + bool set_receiver_monitor_external_synchronization_status(resources& resources, nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + { + return nc::details::set_monitor_status(resources, oid, external_synchronization_status, external_synchronization_status_message, + nc_receiver_monitor_external_synchronization_status_property_id, + nc_receiver_monitor_external_synchronization_status_message_property_id, + nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, + nmos::fields::nc::external_synchronization_status_pending_received_time, + get_control_protocol_class_descriptor, + gate); + } - bool set_receiver_monitor_connection_status_with_delay(resources& resources, nc_oid oid, nmos::nc_connection_status::status connection_status, const utility::string_t& connection_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - const auto now_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - - return nc::details::set_monitor_status_with_delay(resources, oid, connection_status, connection_status_message, - nc_receiver_monitor_connection_status_property_id, - nc_receiver_monitor_connection_status_message_property_id, - nc_receiver_monitor_connection_status_transition_counter_property_id, - nmos::fields::nc::connection_status_pending, - nmos::fields::nc::connection_status_message_pending, - nmos::fields::nc::connection_status_pending_received_time, - now_time, - monitor_status_pending, - get_control_protocol_class_descriptor, - gate); - } + bool set_receiver_monitor_external_synchronization_status_with_delay(resources& resources, nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + { + const auto now_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + + return nc::details::set_monitor_status_with_delay(resources, oid, external_synchronization_status, external_synchronization_status_message, + nc_receiver_monitor_external_synchronization_status_property_id, + nc_receiver_monitor_external_synchronization_status_message_property_id, + nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, + nmos::fields::nc::external_synchronization_status_pending, + nmos::fields::nc::external_synchronization_status_message_pending, + nmos::fields::nc::external_synchronization_status_pending_received_time, + now_time, + monitor_status_pending, + get_control_protocol_class_descriptor, + gate); + } - // Set external synchronization status and external synchronization status message - bool set_receiver_monitor_external_synchronization_status(resources& resources, nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - return nc::details::set_monitor_status(resources, oid, external_synchronization_status, external_synchronization_status_message, - nc_receiver_monitor_external_synchronization_status_property_id, - nc_receiver_monitor_external_synchronization_status_message_property_id, - nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, - nmos::fields::nc::external_synchronization_status_pending_received_time, - get_control_protocol_class_descriptor, - gate); - } + // Set stream status and stream status message + bool set_receiver_monitor_stream_status(resources& resources, nc_oid oid, nmos::nc_stream_status::status stream_status, const utility::string_t& stream_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + { + return nc::details::set_monitor_status(resources, oid, stream_status, stream_status_message, + nc_receiver_monitor_stream_status_property_id, + nc_receiver_monitor_stream_status_message_property_id, + nc_receiver_monitor_stream_status_transition_counter_property_id, + nmos::fields::nc::stream_status_pending_received_time, + get_control_protocol_class_descriptor, + gate); + } - bool set_receiver_monitor_external_synchronization_status_with_delay(resources& resources, nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - const auto now_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - - return nc::details::set_monitor_status_with_delay(resources, oid, external_synchronization_status, external_synchronization_status_message, - nc_receiver_monitor_external_synchronization_status_property_id, - nc_receiver_monitor_external_synchronization_status_message_property_id, - nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, - nmos::fields::nc::external_synchronization_status_pending, - nmos::fields::nc::external_synchronization_status_message_pending, - nmos::fields::nc::external_synchronization_status_pending_received_time, - now_time, - monitor_status_pending, - get_control_protocol_class_descriptor, - gate); - } + bool set_receiver_monitor_stream_status_with_delay(resources& resources, nc_oid oid, nmos::nc_stream_status::status stream_status, const utility::string_t& stream_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + { + const auto now_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + + return nc::details::set_monitor_status_with_delay(resources, oid, stream_status, stream_status_message, + nc_receiver_monitor_stream_status_property_id, + nc_receiver_monitor_stream_status_message_property_id, + nc_receiver_monitor_stream_status_transition_counter_property_id, + nmos::fields::nc::stream_status_pending, + nmos::fields::nc::stream_status_message_pending, + nmos::fields::nc::stream_status_pending_received_time, + now_time, + monitor_status_pending, + get_control_protocol_class_descriptor, + gate); + } - // Set stream status and stream status message - bool set_receiver_monitor_stream_status(resources& resources, nc_oid oid, nmos::nc_stream_status::status stream_status, const utility::string_t& stream_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - return nc::details::set_monitor_status(resources, oid, stream_status, stream_status_message, - nc_receiver_monitor_stream_status_property_id, - nc_receiver_monitor_stream_status_message_property_id, - nc_receiver_monitor_stream_status_transition_counter_property_id, - nmos::fields::nc::stream_status_pending_received_time, - get_control_protocol_class_descriptor, - gate); - } + // Set synchronization source id + bool set_monitor_synchronization_source_id(resources& resources, nc_oid oid, const bst::optional& source_id_, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + { + web::json::value source_id = source_id_ ? web::json::value::string(*source_id_) : web::json::value{}; + return set_control_protocol_property_and_notify(resources, oid, nc_receiver_monitor_synchronization_source_id_property_id, source_id, get_control_protocol_class_descriptor, gate); + } - bool set_receiver_monitor_stream_status_with_delay(resources& resources, nc_oid oid, nmos::nc_stream_status::status stream_status, const utility::string_t& stream_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - const auto now_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - - return nc::details::set_monitor_status_with_delay(resources, oid, stream_status, stream_status_message, - nc_receiver_monitor_stream_status_property_id, - nc_receiver_monitor_stream_status_message_property_id, - nc_receiver_monitor_stream_status_transition_counter_property_id, - nmos::fields::nc::stream_status_pending, - nmos::fields::nc::stream_status_message_pending, - nmos::fields::nc::stream_status_pending_received_time, - now_time, - monitor_status_pending, - get_control_protocol_class_descriptor, - gate); - } + bool activate_monitor(resources& resources, nc_oid oid, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, slog::base_gate& gate) + { + // A monitor is expected to go through a period of instability upon activation. Therefore, on monitor activation + // domain specific statuses offering an Inactive option MUST transition immediately to the Healthy state. + // Furthermore, after activation, as long as the monitor isn’t being deactivated, it MUST delay the reporting + // of non Healthy states for the duration specified by statusReportingDelay, and then transition to any other appropriate state. + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found && nc::is_nc_status_monitor(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) + { + const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)); - // Set synchronization source id - bool set_monitor_synchronization_source_id(resources& resources, nc_oid oid, const bst::optional& source_id_, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - web::json::value source_id = source_id_ ? web::json::value::string(*source_id_) : web::json::value{}; - return set_control_protocol_property_and_notify(resources, oid, nc_receiver_monitor_synchronization_source_id_property_id, source_id, get_control_protocol_class_descriptor, gate); - } + auto activation_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + auto succeed = set_control_protocol_property(resources, oid, nmos::fields::nc::monitor_activation_time, activation_time, gate); + // If autoResetCountersAndMessages set to true then reset the transition counters + bool auto_reset_monitor{false}; - bool activate_monitor(resources& resources, nc_oid oid, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, slog::base_gate& gate) - { - // A monitor is expected to go through a period of instability upon activation. Therefore, on monitor activation - // domain specific statuses offering an Inactive option MUST transition immediately to the Healthy state. - // Furthermore, after activation, as long as the monitor isn’t being deactivated, it MUST delay the reporting - // of non Healthy states for the duration specified by statusReportingDelay, and then transition to any other appropriate state. - const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found && nc::is_nc_status_monitor(details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) - { - const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)); + if (nc::is_nc_sender_monitor(class_id)) + { + if (succeed) succeed = set_sender_monitor_transmission_status(resources, oid, nmos::nc_transmission_status::status::healthy, U("Sender activated"), get_control_protocol_class_descriptor, gate); + if (succeed) succeed = set_sender_monitor_essence_status(resources, oid, nmos::nc_essence_status::status::healthy, U("Sender activated"), get_control_protocol_class_descriptor, gate); + } + else + { + if (succeed) succeed = set_receiver_monitor_connection_status(resources, oid, nmos::nc_connection_status::status::healthy, U("Receiver activated"), get_control_protocol_class_descriptor, gate); + if (succeed) succeed = set_receiver_monitor_stream_status(resources, oid, nmos::nc_stream_status::status::healthy, U("Receiver activated"), get_control_protocol_class_descriptor, gate); + } - auto activation_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - auto succeed = set_control_protocol_property(resources, oid, nmos::fields::nc::monitor_activation_time, activation_time, gate); - // If autoResetCountersAndMessages set to true then reset the transition counters - bool auto_reset_monitor{false}; + if (succeed) + { + auto auto_reset_property_id = nc::is_nc_sender_monitor(class_id) ? nmos::nc_sender_monitor_auto_reset_monitor_property_id : nmos::nc_receiver_monitor_auto_reset_monitor_property_id; + auto auto_reset_monitor_ = get_control_protocol_property(resources, oid, auto_reset_property_id, get_control_protocol_class_descriptor, gate); + if (auto_reset_monitor_.is_null()) succeed = false; + else auto_reset_monitor = auto_reset_monitor_.as_bool(); + } - if (nc::is_nc_sender_monitor(class_id)) - { - if (succeed) succeed = set_sender_monitor_transmission_status(resources, oid, nmos::nc_transmission_status::status::healthy, U("Sender activated"), get_control_protocol_class_descriptor, gate); - if (succeed) succeed = set_sender_monitor_essence_status(resources, oid, nmos::nc_essence_status::status::healthy, U("Sender activated"), get_control_protocol_class_descriptor, gate); + if (succeed && auto_reset_monitor) + { + auto reset_monitor_method_id = nc::is_nc_sender_monitor(class_id) ? nmos::nc_sender_monitor_reset_monitor_method_id : nmos::nc_receiver_monitor_reset_monitor_method_id; + // find the method_handler for the reset monitor method + auto method = get_control_protocol_method_descriptor(class_id, reset_monitor_method_id); + auto& nc_method_descriptor = method.first; + auto& reset_monitor = method.second; + if (reset_monitor) + { + // this callback should not throw exceptions + const auto method_result = reset_monitor(resources, *found, web::json::value::null(), nmos::fields::nc::is_deprecated(nc_method_descriptor), gate); + const auto status = nmos::fields::nc::status(method_result); + succeed = (nmos::nc_method_status::ok == status || nmos::nc_method_status::property_deprecated == status || nmos::nc_method_status::method_deprecated == status); + } + } + if (succeed) slog::log(gate, SLOG_FLF) << "Activating monitor oid: " << oid; + else slog::log(gate, SLOG_FLF) << "Fail to activating monitor oid: " << oid; + + return succeed; } else { - if (succeed) succeed = set_receiver_monitor_connection_status(resources, oid, nmos::nc_connection_status::status::healthy, U("Receiver activated"), get_control_protocol_class_descriptor, gate); - if (succeed) succeed = set_receiver_monitor_stream_status(resources, oid, nmos::nc_stream_status::status::healthy, U("Receiver activated"), get_control_protocol_class_descriptor, gate); + // should never happen + slog::log(gate, SLOG_FLF) << "Invalid logic found in activate monitor oid: " << oid; } + return false; + } - if (succeed) + bool deactivate_monitor(resources& resources, nc_oid oid, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + { + const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); + if (resources.end() != found && nc::is_nc_status_monitor(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) { - auto auto_reset_property_id = nc::is_nc_sender_monitor(class_id) ? nmos::nc_sender_monitor_auto_reset_monitor_property_id : nmos::nc_receiver_monitor_auto_reset_monitor_property_id; - auto auto_reset_monitor_ = get_control_protocol_property(resources, oid, auto_reset_property_id, get_control_protocol_class_descriptor, gate); - if (auto_reset_monitor_.is_null()) succeed = false; - else auto_reset_monitor = auto_reset_monitor_.as_bool(); - } + const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)); - if (succeed && auto_reset_monitor) - { - auto reset_monitor_method_id = nc::is_nc_sender_monitor(class_id) ? nmos::nc_sender_monitor_reset_monitor_method_id : nmos::nc_receiver_monitor_reset_monitor_method_id; - // find the method_handler for the reset monitor method - auto method = get_control_protocol_method_descriptor(class_id, reset_monitor_method_id); - auto& nc_method_descriptor = method.first; - auto& reset_monitor = method.second; - if (reset_monitor) + auto succeed = set_control_protocol_property(resources, oid, nmos::fields::nc::monitor_activation_time, web::json::value::number(0), gate); + + if (nc::is_nc_sender_monitor(class_id)) { - // this callback should not throw exceptions - const auto method_result = reset_monitor(resources, *found, web::json::value::null(), nmos::fields::nc::is_deprecated(nc_method_descriptor), gate); - const auto status = nmos::fields::nc::status(method_result); - succeed = (nmos::nc_method_status::ok == status || nmos::nc_method_status::property_deprecated == status || nmos::nc_method_status::method_deprecated == status); + if (succeed) succeed = set_sender_monitor_transmission_status(resources, oid, nmos::nc_transmission_status::status::inactive, U("Sender deactivated"), get_control_protocol_class_descriptor, gate); + if (succeed) succeed = set_sender_monitor_essence_status(resources, oid, nmos::nc_essence_status::status::inactive, U("Sender deactivated"), get_control_protocol_class_descriptor, gate); } + else + { + if (succeed) succeed = set_receiver_monitor_connection_status(resources, oid, nmos::nc_connection_status::status::inactive, U("Receiver deactivated"), get_control_protocol_class_descriptor, gate); + if (succeed) succeed = set_receiver_monitor_stream_status(resources, oid, nmos::nc_stream_status::status::inactive, U("Receiver deactivated"), get_control_protocol_class_descriptor, gate); + } + return succeed; + } + else + { + // should never happen + slog::log(gate, SLOG_FLF) << "Invalid logic found in deactivate monitor oid: " << oid; } - if (succeed) slog::log(gate, SLOG_FLF) << "Activating monitor oid: " << oid; - else slog::log(gate, SLOG_FLF) << "Fail to activating monitor oid: " << oid; + return false; + } - return succeed; + // Set link status and link status message + bool set_sender_monitor_link_status(resources& resources, nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + { + return nc::details::set_monitor_status(resources, oid, link_status, link_status_message, + nc_sender_monitor_link_status_property_id, + nc_sender_monitor_link_status_message_property_id, + nc_sender_monitor_link_status_transition_counter_property_id, + nmos::fields::nc::link_status_pending_received_time, + get_control_protocol_class_descriptor, + gate); } - else + // Set link status and status message and apply status reporting delay + bool set_sender_monitor_link_status_with_delay(resources& resources, nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { - // should never happen - slog::log(gate, SLOG_FLF) << "Invalid logic found in activate monitor oid: " << oid; + const auto now_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + + return nc::details::set_monitor_status_with_delay(resources, oid, link_status, link_status_message, + nc_sender_monitor_link_status_property_id, + nc_sender_monitor_link_status_message_property_id, + nc_sender_monitor_link_status_transition_counter_property_id, + nmos::fields::nc::link_status_pending, + nmos::fields::nc::link_status_message_pending, + nmos::fields::nc::link_status_pending_received_time, + now_time, + monitor_status_pending, + get_control_protocol_class_descriptor, + gate); } - return false; - } - bool deactivate_monitor(resources& resources, nc_oid oid, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found && nc::is_nc_status_monitor(details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) + // Set transmission status and transmission status message + bool set_sender_monitor_transmission_status(resources& resources, nc_oid oid, nmos::nc_transmission_status::status transmission_status, const utility::string_t& transmission_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { - const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)); - - auto succeed = set_control_protocol_property(resources, oid, nmos::fields::nc::monitor_activation_time, web::json::value::number(0), gate); - - if (nc::is_nc_sender_monitor(class_id)) - { - if (succeed) succeed = set_sender_monitor_transmission_status(resources, oid, nmos::nc_transmission_status::status::inactive, U("Sender deactivated"), get_control_protocol_class_descriptor, gate); - if (succeed) succeed = set_sender_monitor_essence_status(resources, oid, nmos::nc_essence_status::status::inactive, U("Sender deactivated"), get_control_protocol_class_descriptor, gate); - } - else - { - if (succeed) succeed = set_receiver_monitor_connection_status(resources, oid, nmos::nc_connection_status::status::inactive, U("Receiver deactivated"), get_control_protocol_class_descriptor, gate); - if (succeed) succeed = set_receiver_monitor_stream_status(resources, oid, nmos::nc_stream_status::status::inactive, U("Receiver deactivated"), get_control_protocol_class_descriptor, gate); - } - return succeed; + return nc::details::set_monitor_status(resources, oid, transmission_status, transmission_status_message, + nc_sender_monitor_transmission_status_property_id, + nc_sender_monitor_transmission_status_message_property_id, + nc_sender_monitor_transmission_status_transition_counter_property_id, + nmos::fields::nc::connection_status_pending_received_time, + get_control_protocol_class_descriptor, + gate); } - else + // Set transmission status and status message and apply status reporting delay + bool set_sender_monitor_transmission_status_with_delay(resources& resources, nc_oid oid, nmos::nc_transmission_status::status transmission_status, const utility::string_t& transmission_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { - // should never happen - slog::log(gate, SLOG_FLF) << "Invalid logic found in deactivate monitor oid: " << oid; + const auto now_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + + return nc::details::set_monitor_status_with_delay(resources, oid, transmission_status, transmission_status_message, + nc_sender_monitor_transmission_status_property_id, + nc_sender_monitor_transmission_status_message_property_id, + nc_sender_monitor_transmission_status_transition_counter_property_id, + nmos::fields::nc::transmission_status_pending, + nmos::fields::nc::transmission_status_message_pending, + nmos::fields::nc::transmission_status_pending_received_time, + now_time, + monitor_status_pending, + get_control_protocol_class_descriptor, + gate); } - return false; - } - // Set link status and link status message - bool set_sender_monitor_link_status(resources& resources, nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - return nc::details::set_monitor_status(resources, oid, link_status, link_status_message, - nc_sender_monitor_link_status_property_id, - nc_sender_monitor_link_status_message_property_id, - nc_sender_monitor_link_status_transition_counter_property_id, - nmos::fields::nc::link_status_pending_received_time, - get_control_protocol_class_descriptor, - gate); - } - // Set link status and status message and apply status reporting delay - bool set_sender_monitor_link_status_with_delay(resources& resources, nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - const auto now_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - - return nc::details::set_monitor_status_with_delay(resources, oid, link_status, link_status_message, - nc_sender_monitor_link_status_property_id, - nc_sender_monitor_link_status_message_property_id, - nc_sender_monitor_link_status_transition_counter_property_id, - nmos::fields::nc::link_status_pending, - nmos::fields::nc::link_status_message_pending, - nmos::fields::nc::link_status_pending_received_time, - now_time, - monitor_status_pending, - get_control_protocol_class_descriptor, - gate); - } - - // Set transmission status and transmission status message - bool set_sender_monitor_transmission_status(resources& resources, nc_oid oid, nmos::nc_transmission_status::status transmission_status, const utility::string_t& transmission_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - return nc::details::set_monitor_status(resources, oid, transmission_status, transmission_status_message, - nc_sender_monitor_transmission_status_property_id, - nc_sender_monitor_transmission_status_message_property_id, - nc_sender_monitor_transmission_status_transition_counter_property_id, - nmos::fields::nc::connection_status_pending_received_time, - get_control_protocol_class_descriptor, - gate); - } - // Set transmission status and status message and apply status reporting delay - bool set_sender_monitor_transmission_status_with_delay(resources& resources, nc_oid oid, nmos::nc_transmission_status::status transmission_status, const utility::string_t& transmission_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - const auto now_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - - return nc::details::set_monitor_status_with_delay(resources, oid, transmission_status, transmission_status_message, - nc_sender_monitor_transmission_status_property_id, - nc_sender_monitor_transmission_status_message_property_id, - nc_sender_monitor_transmission_status_transition_counter_property_id, - nmos::fields::nc::transmission_status_pending, - nmos::fields::nc::transmission_status_message_pending, - nmos::fields::nc::transmission_status_pending_received_time, - now_time, - monitor_status_pending, - get_control_protocol_class_descriptor, - gate); - } + // Set external synchronization status and external synchronization status message + bool set_sender_monitor_external_synchronization_status(resources& resources, nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + { + return nc::details::set_monitor_status(resources, oid, external_synchronization_status, external_synchronization_status_message, + nc_sender_monitor_external_synchronization_status_property_id, + nc_sender_monitor_external_synchronization_status_message_property_id, + nc_sender_monitor_external_synchronization_status_transition_counter_property_id, + nmos::fields::nc::external_synchronization_status_pending_received_time, + get_control_protocol_class_descriptor, + gate); + } + // Set external synchronization status and status message and apply status reporting delay + bool set_sender_monitor_external_synchronization_status_with_delay(resources& resources, nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + { + const auto now_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + + return nc::details::set_monitor_status_with_delay(resources, oid, external_synchronization_status, external_synchronization_status_message, + nc_sender_monitor_external_synchronization_status_property_id, + nc_sender_monitor_external_synchronization_status_message_property_id, + nc_sender_monitor_external_synchronization_status_transition_counter_property_id, + nmos::fields::nc::external_synchronization_status_pending, + nmos::fields::nc::external_synchronization_status_message_pending, + nmos::fields::nc::external_synchronization_status_pending_received_time, + now_time, + monitor_status_pending, + get_control_protocol_class_descriptor, + gate); + } - // Set external synchronization status and external synchronization status message - bool set_sender_monitor_external_synchronization_status(resources& resources, nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - return nc::details::set_monitor_status(resources, oid, external_synchronization_status, external_synchronization_status_message, - nc_sender_monitor_external_synchronization_status_property_id, - nc_sender_monitor_external_synchronization_status_message_property_id, - nc_sender_monitor_external_synchronization_status_transition_counter_property_id, - nmos::fields::nc::external_synchronization_status_pending_received_time, - get_control_protocol_class_descriptor, - gate); - } - // Set external synchronization status and status message and apply status reporting delay - bool set_sender_monitor_external_synchronization_status_with_delay(resources& resources, nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - const auto now_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - - return nc::details::set_monitor_status_with_delay(resources, oid, external_synchronization_status, external_synchronization_status_message, - nc_sender_monitor_external_synchronization_status_property_id, - nc_sender_monitor_external_synchronization_status_message_property_id, - nc_sender_monitor_external_synchronization_status_transition_counter_property_id, - nmos::fields::nc::external_synchronization_status_pending, - nmos::fields::nc::external_synchronization_status_message_pending, - nmos::fields::nc::external_synchronization_status_pending_received_time, - now_time, - monitor_status_pending, - get_control_protocol_class_descriptor, - gate); + // Set essence status and stream status message + bool set_sender_monitor_essence_status(resources& resources, nc_oid oid, nmos::nc_essence_status::status essence_status, const utility::string_t& essence_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + { + return nc::details::set_monitor_status(resources, oid, essence_status, essence_status_message, + nc_sender_monitor_essence_status_property_id, + nc_sender_monitor_essence_status_message_property_id, + nc_sender_monitor_essence_status_transition_counter_property_id, + nmos::fields::nc::stream_status_pending_received_time, + get_control_protocol_class_descriptor, + gate); + } + // Set essence status and status message and apply status reporting delay + bool set_sender_monitor_essence_status_with_delay(resources& resources, nc_oid oid, nmos::nc_essence_status::status essence_status, const utility::string_t& essence_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + { + const auto now_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + + return nc::details::set_monitor_status_with_delay(resources, oid, essence_status, essence_status_message, + nc_sender_monitor_essence_status_property_id, + nc_sender_monitor_essence_status_message_property_id, + nc_sender_monitor_essence_status_transition_counter_property_id, + nmos::fields::nc::essence_status_pending, + nmos::fields::nc::essence_status_message_pending, + nmos::fields::nc::essence_status_pending_received_time, + now_time, + monitor_status_pending, + get_control_protocol_class_descriptor, + gate); + } } - // Set essence status and stream status message - bool set_sender_monitor_essence_status(resources& resources, nc_oid oid, nmos::nc_essence_status::status essence_status, const utility::string_t& essence_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - return nc::details::set_monitor_status(resources, oid, essence_status, essence_status_message, - nc_sender_monitor_essence_status_property_id, - nc_sender_monitor_essence_status_message_property_id, - nc_sender_monitor_essence_status_transition_counter_property_id, - nmos::fields::nc::stream_status_pending_received_time, - get_control_protocol_class_descriptor, - gate); - } - // Set essence status and status message and apply status reporting delay - bool set_sender_monitor_essence_status_with_delay(resources& resources, nc_oid oid, nmos::nc_essence_status::status essence_status, const utility::string_t& essence_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - const auto now_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - - return nc::details::set_monitor_status_with_delay(resources, oid, essence_status, essence_status_message, - nc_sender_monitor_essence_status_property_id, - nc_sender_monitor_essence_status_message_property_id, - nc_sender_monitor_essence_status_transition_counter_property_id, - nmos::fields::nc::essence_status_pending, - nmos::fields::nc::essence_status_message_pending, - nmos::fields::nc::essence_status_pending_received_time, - now_time, - monitor_status_pending, - get_control_protocol_class_descriptor, - gate); - } } diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index 6a9a4c7b2..559c45c4f 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -127,70 +127,70 @@ namespace nmos void method_parameters_contraints_validation(const web::json::value& arguments, const web::json::value& nc_method_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor); resources::const_iterator find_touchpoint_resource(const resources& resources, const resource& resource); - } - // insert 'value changed', 'sequence item added', 'sequence item changed' or 'sequence item removed' notification events into all grains whose subscriptions match the specified version, type and "pre" or "post" values - void insert_notification_events(resources& resources, const api_version& version, const api_version& downgrade_version, const type& type, const web::json::value& pre, const web::json::value& post, const web::json::value& event); + // insert 'value changed', 'sequence item added', 'sequence item changed' or 'sequence item removed' notification events into all grains whose subscriptions match the specified version, type and "pre" or "post" values + void insert_notification_events(resources& resources, const api_version& version, const api_version& downgrade_version, const type& type, const web::json::value& pre, const web::json::value& post, const web::json::value& event); - // get property value given oid and property_id - web::json::value get_control_protocol_property(const resources& resources, nc_oid oid, const nc_property_id& property_id, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // get property value given oid and property_id + web::json::value get_control_protocol_property(const resources& resources, nc_oid oid, const nc_property_id& property_id, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // set property value given oid and property_id and notify - bool set_control_protocol_property_and_notify(resources& resources, nc_oid oid, const nc_property_id& property_id, const web::json::value& value, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // set property value given oid and property_id and notify + bool set_control_protocol_property_and_notify(resources& resources, nc_oid oid, const nc_property_id& property_id, const web::json::value& value, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // set hidden property but don't notify, as property isn't part of class definition - bool set_control_protocol_property(resources& resources, nc_oid oid, const utility::string_t& property_name, const web::json::value& value, slog::base_gate& gate); + // set hidden property but don't notify, as property isn't part of class definition + bool set_control_protocol_property(resources& resources, nc_oid oid, const utility::string_t& property_name, const web::json::value& value, slog::base_gate& gate); - // Set link status and link status message - bool set_receiver_monitor_link_status(resources& resources, nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set link status and status message and apply status reporting delay - bool set_receiver_monitor_link_status_with_delay(resources& resources, nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set link status and link status message + bool set_receiver_monitor_link_status(resources& resources, nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set link status and status message and apply status reporting delay + bool set_receiver_monitor_link_status_with_delay(resources& resources, nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set connection status and connection status message - bool set_receiver_monitor_connection_status(resources& resources, nc_oid oid, nmos::nc_connection_status::status connection_status, const utility::string_t& connection_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set connection status and status message and apply status reporting delay - bool set_receiver_monitor_connection_status_with_delay(resources& resources, nc_oid oid, nmos::nc_connection_status::status connection_status, const utility::string_t& connection_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set connection status and connection status message + bool set_receiver_monitor_connection_status(resources& resources, nc_oid oid, nmos::nc_connection_status::status connection_status, const utility::string_t& connection_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set connection status and status message and apply status reporting delay + bool set_receiver_monitor_connection_status_with_delay(resources& resources, nc_oid oid, nmos::nc_connection_status::status connection_status, const utility::string_t& connection_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set external synchronization status and external synchronization status message - bool set_receiver_monitor_external_synchronization_status(resources& resources, nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set external synchronization status and status message and apply status reporting delay - bool set_receiver_monitor_external_synchronization_status_with_delay(resources& resources, nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set external synchronization status and external synchronization status message + bool set_receiver_monitor_external_synchronization_status(resources& resources, nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set external synchronization status and status message and apply status reporting delay + bool set_receiver_monitor_external_synchronization_status_with_delay(resources& resources, nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set stream status and stream status message - bool set_receiver_monitor_stream_status(resources& resources, nc_oid oid, nmos::nc_stream_status::status stream_status, const utility::string_t& stream_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set stream status and status message and apply status reporting delay - bool set_receiver_monitor_stream_status_with_delay(resources& resources, nc_oid oid, nmos::nc_stream_status::status stream_status, const utility::string_t& stream_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set stream status and stream status message + bool set_receiver_monitor_stream_status(resources& resources, nc_oid oid, nmos::nc_stream_status::status stream_status, const utility::string_t& stream_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set stream status and status message and apply status reporting delay + bool set_receiver_monitor_stream_status_with_delay(resources& resources, nc_oid oid, nmos::nc_stream_status::status stream_status, const utility::string_t& stream_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set synchronization source id - bool set_monitor_synchronization_source_id(resources& resources, nc_oid oid, const bst::optional& source_id, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set synchronization source id + bool set_monitor_synchronization_source_id(resources& resources, nc_oid oid, const bst::optional& source_id, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Call when monitor is activated - bool activate_monitor(resources& resources, nc_oid oid, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, slog::base_gate& gate); - // Call when monitor is deactivated - bool deactivate_monitor(resources& resources, nc_oid oid, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Call when monitor is activated + bool activate_monitor(resources& resources, nc_oid oid, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, slog::base_gate& gate); + // Call when monitor is deactivated + bool deactivate_monitor(resources& resources, nc_oid oid, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set link status and link status message - bool set_sender_monitor_link_status(resources& resources, nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set link status and status message and apply status reporting delay - bool set_sender_monitor_link_status_with_delay(resources& resources, nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set link status and link status message + bool set_sender_monitor_link_status(resources& resources, nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set link status and status message and apply status reporting delay + bool set_sender_monitor_link_status_with_delay(resources& resources, nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set transmission status and transmission status message - bool set_sender_monitor_transmission_status(resources& resources, nc_oid oid, nmos::nc_transmission_status::status transmission_status, const utility::string_t& transmission_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set transmission status and status message and apply status reporting delay - bool set_sender_monitor_transmission_status_with_delay(resources& resources, nc_oid oid, nmos::nc_transmission_status::status transmission_status, const utility::string_t& transmission_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set transmission status and transmission status message + bool set_sender_monitor_transmission_status(resources& resources, nc_oid oid, nmos::nc_transmission_status::status transmission_status, const utility::string_t& transmission_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set transmission status and status message and apply status reporting delay + bool set_sender_monitor_transmission_status_with_delay(resources& resources, nc_oid oid, nmos::nc_transmission_status::status transmission_status, const utility::string_t& transmission_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set external synchronization status and external synchronization status message - bool set_sender_monitor_external_synchronization_status(resources& resources, nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set external synchronization status and status message and apply status reporting delay - bool set_sender_monitor_external_synchronization_status_with_delay(resources& resources, nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set external synchronization status and external synchronization status message + bool set_sender_monitor_external_synchronization_status(resources& resources, nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set external synchronization status and status message and apply status reporting delay + bool set_sender_monitor_external_synchronization_status_with_delay(resources& resources, nc_oid oid, nmos::nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set essence status and stream status message - bool set_sender_monitor_essence_status(resources& resources, nc_oid oid, nmos::nc_essence_status::status essence_status, const utility::string_t& essence_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set essence status and status message and apply status reporting delay - bool set_sender_monitor_essence_status_with_delay(resources& resources, nc_oid oid, nmos::nc_essence_status::status essence_status, const utility::string_t& essence_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set essence status and stream status message + bool set_sender_monitor_essence_status(resources& resources, nc_oid oid, nmos::nc_essence_status::status essence_status, const utility::string_t& essence_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set essence status and status message and apply status reporting delay + bool set_sender_monitor_essence_status_with_delay(resources& resources, nc_oid oid, nmos::nc_essence_status::status essence_status, const utility::string_t& essence_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Used to get "hidden" resource properties - web::json::value get_control_protocol_property(const resources& resources, nc_oid oid, const utility::string_t& property_name, slog::base_gate& gate); + // Get property by name, rather than by property id. Used to get "hidden" resource properties + web::json::value get_control_protocol_property(const resources& resources, nc_oid oid, const utility::string_t& property_name, slog::base_gate& gate); + } } #endif \ No newline at end of file diff --git a/Development/nmos/test/control_protocol_utils_test.cpp b/Development/nmos/test/control_protocol_utils_test.cpp index 226a08b8c..d66e4bbeb 100644 --- a/Development/nmos/test/control_protocol_utils_test.cpp +++ b/Development/nmos/test/control_protocol_utils_test.cpp @@ -36,21 +36,21 @@ BST_TEST_CASE(testGetSetControlProtocolProperty) insert_resource(resources, std::move(root_block)); insert_resource(resources, std::move(class_manager)); - auto oid = nmos::get_control_protocol_property(resources, class_manager_oid, nmos::nc_object_oid_property_id, get_control_protocol_class_descriptor, gate); + auto oid = nmos::nc::get_control_protocol_property(resources, class_manager_oid, nmos::nc_object_oid_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(class_manager_oid, static_cast(oid.as_integer())); - auto role = get_control_protocol_property(resources, class_manager_oid, nmos::nc_object_role_property_id, get_control_protocol_class_descriptor, gate); + auto role = nmos::nc::get_control_protocol_property(resources, class_manager_oid, nmos::nc_object_role_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(U("ClassManager"), role.as_string()); auto test_label = U("ThisIsATest"); - bool result = set_control_protocol_property_and_notify(resources, class_manager_oid, nmos::nc_object_user_label_property_id, web::json::value::string(test_label), get_control_protocol_class_descriptor, gate); + bool result = nmos::nc::set_control_protocol_property_and_notify(resources, class_manager_oid, nmos::nc_object_user_label_property_id, web::json::value::string(test_label), get_control_protocol_class_descriptor, gate); BST_REQUIRE(result); - auto label = get_control_protocol_property(resources, class_manager_oid, nmos::nc_object_user_label_property_id, get_control_protocol_class_descriptor, gate); + auto label = nmos::nc::get_control_protocol_property(resources, class_manager_oid, nmos::nc_object_user_label_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(test_label, label.as_string()); } @@ -98,37 +98,37 @@ BST_TEST_CASE(testSetReceiverMonitorStatuses) auto stream_status_message = U("Stream status healthy"); auto expected_stream_status_transition_counter = 0; - BST_REQUIRE(set_receiver_monitor_link_status(resources, monitor_oid, link_status, link_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_receiver_monitor_connection_status(resources, monitor_oid, connection_status, connection_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_receiver_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_receiver_monitor_stream_status(resources, monitor_oid, stream_status, stream_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_receiver_monitor_link_status(resources, monitor_oid, link_status, link_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_receiver_monitor_connection_status(resources, monitor_oid, connection_status, connection_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_receiver_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_receiver_monitor_stream_status(resources, monitor_oid, stream_status, stream_status_message, get_control_protocol_class_descriptor, gate)); - auto actual_link_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status, actual_link_status.as_integer()); - auto actual_link_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status_message, actual_link_status_message.as_string()); - auto actual_link_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_link_status_transition_counter, actual_link_status_transition_counter.as_integer()); - auto actual_connection_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(connection_status, actual_connection_status.as_integer()); - auto actual_connection_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(connection_status_message, actual_connection_status_message.as_string()); - auto actual_connection_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_connection_status_transition_counter, actual_connection_status_transition_counter.as_integer()); - auto actual_external_synchronization_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status, actual_external_synchronization_status.as_integer()); - auto actual_external_synchronization_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status_message, actual_external_synchronization_status_message.as_string()); - auto actual_external_synchronization_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_external_synchronization_status_transition_counter, actual_external_synchronization_status_transition_counter.as_integer()); - auto actual_stream_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(stream_status, actual_stream_status.as_integer()); - auto actual_stream_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(stream_status_message, actual_stream_status_message.as_string()); - auto actual_stream_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_stream_status_transition_counter, actual_stream_status_transition_counter.as_integer()); - auto overall_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::inactive, overall_status.as_integer()); } { @@ -145,37 +145,37 @@ BST_TEST_CASE(testSetReceiverMonitorStatuses) auto stream_status_message = U("Stream status healthy"); auto expected_stream_status_transition_counter = 0; - BST_REQUIRE(set_receiver_monitor_link_status(resources, monitor_oid, link_status, link_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_receiver_monitor_connection_status(resources, monitor_oid, connection_status, connection_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_receiver_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_receiver_monitor_stream_status(resources, monitor_oid, stream_status, stream_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_receiver_monitor_link_status(resources, monitor_oid, link_status, link_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_receiver_monitor_connection_status(resources, monitor_oid, connection_status, connection_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_receiver_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_receiver_monitor_stream_status(resources, monitor_oid, stream_status, stream_status_message, get_control_protocol_class_descriptor, gate)); - auto actual_link_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status, actual_link_status.as_integer()); - auto actual_link_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status_message, actual_link_status_message.as_string()); - auto actual_link_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_link_status_transition_counter, actual_link_status_transition_counter.as_integer()); - auto actual_connection_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(connection_status, actual_connection_status.as_integer()); - auto actual_connection_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(connection_status_message, actual_connection_status_message.as_string()); - auto actual_connection_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_connection_status_transition_counter, actual_connection_status_transition_counter.as_integer()); - auto actual_external_synchronization_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status, actual_external_synchronization_status.as_integer()); - auto actual_external_synchronization_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status_message, actual_external_synchronization_status_message.as_string()); - auto actual_external_synchronization_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_external_synchronization_status_transition_counter, actual_external_synchronization_status_transition_counter.as_integer()); - auto actual_stream_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(stream_status, actual_stream_status.as_integer()); - auto actual_stream_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(stream_status_message, actual_stream_status_message.as_string()); - auto actual_stream_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_stream_status_transition_counter, actual_stream_status_transition_counter.as_integer()); - auto overall_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::healthy, overall_status.as_integer()); } { @@ -192,37 +192,37 @@ BST_TEST_CASE(testSetReceiverMonitorStatuses) auto stream_status_message = U("Stream status healthy"); auto expected_stream_status_transition_counter = 1; - BST_REQUIRE(set_receiver_monitor_link_status(resources, monitor_oid, link_status, link_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_receiver_monitor_connection_status(resources, monitor_oid, connection_status, connection_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_receiver_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_receiver_monitor_stream_status(resources, monitor_oid, stream_status, stream_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_receiver_monitor_link_status(resources, monitor_oid, link_status, link_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_receiver_monitor_connection_status(resources, monitor_oid, connection_status, connection_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_receiver_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_receiver_monitor_stream_status(resources, monitor_oid, stream_status, stream_status_message, get_control_protocol_class_descriptor, gate)); - auto actual_link_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status, actual_link_status.as_integer()); - auto actual_link_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status_message, actual_link_status_message.as_string()); - auto actual_link_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_link_status_transition_counter, actual_link_status_transition_counter.as_integer()); - auto actual_connection_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(connection_status, actual_connection_status.as_integer()); - auto actual_connection_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(connection_status_message, actual_connection_status_message.as_string()); - auto actual_connection_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_connection_status_transition_counter, actual_connection_status_transition_counter.as_integer()); - auto actual_external_synchronization_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status, actual_external_synchronization_status.as_integer()); - auto actual_external_synchronization_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status_message, actual_external_synchronization_status_message.as_string()); - auto actual_external_synchronization_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_external_synchronization_status_transition_counter, actual_external_synchronization_status_transition_counter.as_integer()); - auto actual_stream_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(stream_status, actual_stream_status.as_integer()); - auto actual_stream_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(stream_status_message, actual_stream_status_message.as_string()); - auto actual_stream_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_stream_status_transition_counter, actual_stream_status_transition_counter.as_integer()); - auto overall_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::partially_healthy, overall_status.as_integer()); } { @@ -239,37 +239,37 @@ BST_TEST_CASE(testSetReceiverMonitorStatuses) auto stream_status_message = U("Stream status healthy"); auto expected_stream_status_transition_counter = 1; - BST_REQUIRE(set_receiver_monitor_link_status(resources, monitor_oid, link_status, link_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_receiver_monitor_connection_status(resources, monitor_oid, connection_status, connection_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_receiver_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_receiver_monitor_stream_status(resources, monitor_oid, stream_status, stream_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_receiver_monitor_link_status(resources, monitor_oid, link_status, link_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_receiver_monitor_connection_status(resources, monitor_oid, connection_status, connection_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_receiver_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_receiver_monitor_stream_status(resources, monitor_oid, stream_status, stream_status_message, get_control_protocol_class_descriptor, gate)); - auto actual_link_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status, actual_link_status.as_integer()); - auto actual_link_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status_message, actual_link_status_message.as_string()); - auto actual_link_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_link_status_transition_counter, actual_link_status_transition_counter.as_integer()); - auto actual_connection_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(connection_status, actual_connection_status.as_integer()); - auto actual_connection_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(connection_status_message, actual_connection_status_message.as_string()); - auto actual_connection_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_connection_status_transition_counter, actual_connection_status_transition_counter.as_integer()); - auto actual_external_synchronization_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status, actual_external_synchronization_status.as_integer()); - auto actual_external_synchronization_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status_message, actual_external_synchronization_status_message.as_string()); - auto actual_external_synchronization_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_external_synchronization_status_transition_counter, actual_external_synchronization_status_transition_counter.as_integer()); - auto actual_stream_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(stream_status, actual_stream_status.as_integer()); - auto actual_stream_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(stream_status_message, actual_stream_status_message.as_string()); - auto actual_stream_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_stream_status_transition_counter, actual_stream_status_transition_counter.as_integer()); - auto overall_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::unhealthy, overall_status.as_integer()); } } @@ -305,13 +305,13 @@ BST_TEST_CASE(testSetSynchronizationSourceId) { utility::string_t sync_source_id = U("SYNCID"); - nmos::set_monitor_synchronization_source_id(resources, monitor_oid, sync_source_id, get_control_protocol_class_descriptor, gate); - auto actual_sync_source_id = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_synchronization_source_id_property_id, get_control_protocol_class_descriptor, gate); + nmos::nc::set_monitor_synchronization_source_id(resources, monitor_oid, sync_source_id, get_control_protocol_class_descriptor, gate); + auto actual_sync_source_id = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_synchronization_source_id_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(sync_source_id, actual_sync_source_id.as_string()); } { - nmos::set_monitor_synchronization_source_id(resources, monitor_oid, {}, get_control_protocol_class_descriptor, gate); - auto actual_sync_source_id = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_synchronization_source_id_property_id, get_control_protocol_class_descriptor, gate); + nmos::nc::set_monitor_synchronization_source_id(resources, monitor_oid, {}, get_control_protocol_class_descriptor, gate); + auto actual_sync_source_id = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_synchronization_source_id_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK(actual_sync_source_id.is_null()); } } @@ -359,34 +359,34 @@ BST_TEST_CASE(testActivateDeactivateReceiverMonitor) { // autoResetCounterAndMessages will reset all counters and messages on activate, including calling back into application code - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_auto_reset_monitor_property_id, web::json::value::boolean(true), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_auto_reset_monitor_property_id, web::json::value::boolean(true), get_control_protocol_class_descriptor, gate); uint32_t transition_count = 10; // set transition counters - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); // Do activatation - nmos::activate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, gate); + nmos::nc::activate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, gate); // Check statuses - auto actual_stream_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_stream_status::status::healthy, actual_stream_status.as_integer()); - auto actual_connection_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_stream_status::status::healthy, actual_connection_status.as_integer()); - auto actual_overall_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::healthy, actual_overall_status.as_integer()); // Check transition counters - auto actual_stream_status_transition_counter = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(0, actual_stream_status_transition_counter.as_integer()); - auto actual_link_status_transition_counter = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(0, actual_link_status_transition_counter.as_integer()); - auto actual_connection_status_transition_counter = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(0, actual_connection_status_transition_counter.as_integer()); - auto actual_external_synchronization_status_transition_counter = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(0, actual_external_synchronization_status_transition_counter.as_integer()); // Check that reset_monitor handler was invoked @@ -394,48 +394,48 @@ BST_TEST_CASE(testActivateDeactivateReceiverMonitor) } { // Do deactivation - nmos::deactivate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, gate); + nmos::nc::deactivate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, gate); // Check statuses - auto actual_stream_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_stream_status::status::inactive, actual_stream_status.as_integer()); - auto actual_connection_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_stream_status::status::inactive, actual_connection_status.as_integer()); - auto actual_overall_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::inactive, actual_overall_status.as_integer()); } { reset_monitor_called = false; // disable autoResetCounterAndMessages - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_auto_reset_monitor_property_id, web::json::value::boolean(false), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_auto_reset_monitor_property_id, web::json::value::boolean(false), get_control_protocol_class_descriptor, gate); int32_t transition_count = 10; // set transition counters - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); // Do activatation - nmos::activate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, gate); + nmos::nc::activate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, gate); // Check statuses - auto actual_stream_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_stream_status::status::healthy, actual_stream_status.as_integer()); - auto actual_connection_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_stream_status::status::healthy, actual_connection_status.as_integer()); - auto actual_overall_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::healthy, actual_overall_status.as_integer()); // Check transition counters - auto actual_stream_status_transition_counter = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transition_count, actual_stream_status_transition_counter.as_integer()); - auto actual_link_status_transition_counter = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transition_count, actual_link_status_transition_counter.as_integer()); - auto actual_connection_status_transition_counter = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transition_count, actual_connection_status_transition_counter.as_integer()); - auto actual_external_synchronization_status_transition_counter = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transition_count, actual_external_synchronization_status_transition_counter.as_integer()); // Check that reset_monitor handler was NOT invoked @@ -443,14 +443,14 @@ BST_TEST_CASE(testActivateDeactivateReceiverMonitor) } { // Do deactivation - nmos::deactivate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, gate); + nmos::nc::deactivate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, gate); // Check statuses - auto actual_stream_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_stream_status::status::inactive, actual_stream_status.as_integer()); - auto actual_connection_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_stream_status::status::inactive, actual_connection_status.as_integer()); - auto actual_overall_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::inactive, actual_overall_status.as_integer()); } } @@ -502,37 +502,37 @@ BST_TEST_CASE(testSetSenderMonitorStatuses) auto essence_status_message = U("essence status healthy"); auto expected_essence_status_transition_counter = 0; - BST_REQUIRE(set_sender_monitor_link_status(resources, monitor_oid, link_status, link_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_sender_monitor_transmission_status(resources, monitor_oid, transmission_status, transmission_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_sender_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_sender_monitor_essence_status(resources, monitor_oid, essence_status, essence_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_sender_monitor_link_status(resources, monitor_oid, link_status, link_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_sender_monitor_transmission_status(resources, monitor_oid, transmission_status, transmission_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_sender_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_sender_monitor_essence_status(resources, monitor_oid, essence_status, essence_status_message, get_control_protocol_class_descriptor, gate)); - auto actual_link_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status, actual_link_status.as_integer()); - auto actual_link_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status_message, actual_link_status_message.as_string()); - auto actual_link_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_link_status_transition_counter, actual_link_status_transition_counter.as_integer()); - auto actual_transmission_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transmission_status, actual_transmission_status.as_integer()); - auto actual_transmission_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transmission_status_message, actual_transmission_status_message.as_string()); - auto actual_transmission_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_transmission_status_transition_counter, actual_transmission_status_transition_counter.as_integer()); - auto actual_external_synchronization_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status, actual_external_synchronization_status.as_integer()); - auto actual_external_synchronization_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status_message, actual_external_synchronization_status_message.as_string()); - auto actual_external_synchronization_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_external_synchronization_status_transition_counter, actual_external_synchronization_status_transition_counter.as_integer()); - auto actual_essence_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(essence_status, actual_essence_status.as_integer()); - auto actual_essence_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(essence_status_message, actual_essence_status_message.as_string()); - auto actual_essence_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_essence_status_transition_counter, actual_essence_status_transition_counter.as_integer()); - auto overall_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::inactive, overall_status.as_integer()); } { @@ -549,37 +549,37 @@ BST_TEST_CASE(testSetSenderMonitorStatuses) auto essence_status_message = U("essence status healthy"); auto expected_essence_status_transition_counter = 0; - BST_REQUIRE(set_sender_monitor_link_status(resources, monitor_oid, link_status, link_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_sender_monitor_transmission_status(resources, monitor_oid, transmission_status, transmission_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_sender_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_sender_monitor_essence_status(resources, monitor_oid, essence_status, essence_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_sender_monitor_link_status(resources, monitor_oid, link_status, link_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_sender_monitor_transmission_status(resources, monitor_oid, transmission_status, transmission_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_sender_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_sender_monitor_essence_status(resources, monitor_oid, essence_status, essence_status_message, get_control_protocol_class_descriptor, gate)); - auto actual_link_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status, actual_link_status.as_integer()); - auto actual_link_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status_message, actual_link_status_message.as_string()); - auto actual_link_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_link_status_transition_counter, actual_link_status_transition_counter.as_integer()); - auto actual_transmission_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transmission_status, actual_transmission_status.as_integer()); - auto actual_transmission_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transmission_status_message, actual_transmission_status_message.as_string()); - auto actual_transmission_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_transmission_status_transition_counter, actual_transmission_status_transition_counter.as_integer()); - auto actual_external_synchronization_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status, actual_external_synchronization_status.as_integer()); - auto actual_external_synchronization_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status_message, actual_external_synchronization_status_message.as_string()); - auto actual_external_synchronization_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_external_synchronization_status_transition_counter, actual_external_synchronization_status_transition_counter.as_integer()); - auto actual_essence_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(essence_status, actual_essence_status.as_integer()); - auto actual_essence_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(essence_status_message, actual_essence_status_message.as_string()); - auto actual_essence_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_essence_status_transition_counter, actual_essence_status_transition_counter.as_integer()); - auto overall_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::healthy, overall_status.as_integer()); } { @@ -596,37 +596,37 @@ BST_TEST_CASE(testSetSenderMonitorStatuses) auto essence_status_message = U("essence status healthy"); auto expected_essence_status_transition_counter = 1; - BST_REQUIRE(set_sender_monitor_link_status(resources, monitor_oid, link_status, link_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_sender_monitor_transmission_status(resources, monitor_oid, transmission_status, transmission_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_sender_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_sender_monitor_essence_status(resources, monitor_oid, essence_status, essence_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_sender_monitor_link_status(resources, monitor_oid, link_status, link_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_sender_monitor_transmission_status(resources, monitor_oid, transmission_status, transmission_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_sender_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_sender_monitor_essence_status(resources, monitor_oid, essence_status, essence_status_message, get_control_protocol_class_descriptor, gate)); - auto actual_link_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status, actual_link_status.as_integer()); - auto actual_link_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status_message, actual_link_status_message.as_string()); - auto actual_link_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_link_status_transition_counter, actual_link_status_transition_counter.as_integer()); - auto actual_transmission_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transmission_status, actual_transmission_status.as_integer()); - auto actual_transmission_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transmission_status_message, actual_transmission_status_message.as_string()); - auto actual_transmission_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_transmission_status_transition_counter, actual_transmission_status_transition_counter.as_integer()); - auto actual_external_synchronization_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status, actual_external_synchronization_status.as_integer()); - auto actual_external_synchronization_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status_message, actual_external_synchronization_status_message.as_string()); - auto actual_external_synchronization_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_external_synchronization_status_transition_counter, actual_external_synchronization_status_transition_counter.as_integer()); - auto actual_essence_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(essence_status, actual_essence_status.as_integer()); - auto actual_essence_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(essence_status_message, actual_essence_status_message.as_string()); - auto actual_essence_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_essence_status_transition_counter, actual_essence_status_transition_counter.as_integer()); - auto overall_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::partially_healthy, overall_status.as_integer()); } { @@ -643,37 +643,37 @@ BST_TEST_CASE(testSetSenderMonitorStatuses) auto essence_status_message = U("essence status healthy"); auto expected_essence_status_transition_counter = 1; - BST_REQUIRE(set_sender_monitor_link_status(resources, monitor_oid, link_status, link_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_sender_monitor_transmission_status(resources, monitor_oid, transmission_status, transmission_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_sender_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); - BST_REQUIRE(set_sender_monitor_essence_status(resources, monitor_oid, essence_status, essence_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_sender_monitor_link_status(resources, monitor_oid, link_status, link_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_sender_monitor_transmission_status(resources, monitor_oid, transmission_status, transmission_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_sender_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); + BST_REQUIRE(nmos::nc::set_sender_monitor_essence_status(resources, monitor_oid, essence_status, essence_status_message, get_control_protocol_class_descriptor, gate)); - auto actual_link_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status, actual_link_status.as_integer()); - auto actual_link_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status_message, actual_link_status_message.as_string()); - auto actual_link_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_link_status_transition_counter, actual_link_status_transition_counter.as_integer()); - auto actual_transmission_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transmission_status, actual_transmission_status.as_integer()); - auto actual_transmission_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transmission_status_message, actual_transmission_status_message.as_string()); - auto actual_transmission_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_transmission_status_transition_counter, actual_transmission_status_transition_counter.as_integer()); - auto actual_external_synchronization_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status, actual_external_synchronization_status.as_integer()); - auto actual_external_synchronization_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status_message, actual_external_synchronization_status_message.as_string()); - auto actual_external_synchronization_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_external_synchronization_status_transition_counter, actual_external_synchronization_status_transition_counter.as_integer()); - auto actual_essence_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(essence_status, actual_essence_status.as_integer()); - auto actual_essence_status_message = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(essence_status_message, actual_essence_status_message.as_string()); - auto actual_essence_status_transition_counter = get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_essence_status_transition_counter, actual_essence_status_transition_counter.as_integer()); - auto overall_status = get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::unhealthy, overall_status.as_integer()); } } @@ -721,34 +721,34 @@ BST_TEST_CASE(testActivateDeactivateSenderMonitor) { // autoResetCounterAndMessages will reset all counters and messages on activate, including calling back into application code - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_auto_reset_monitor_property_id, web::json::value::boolean(true), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_auto_reset_monitor_property_id, web::json::value::boolean(true), get_control_protocol_class_descriptor, gate); uint32_t transition_count = 10; // set transition counters - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); // Do activatation - nmos::activate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, gate); + nmos::nc::activate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, gate); // Check statuses - auto actual_essence_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_essence_status::status::healthy, actual_essence_status.as_integer()); - auto actual_transmission_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_essence_status::status::healthy, actual_transmission_status.as_integer()); - auto actual_overall_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::healthy, actual_overall_status.as_integer()); // Check transition counters - auto actual_essence_status_transition_counter = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(0, actual_essence_status_transition_counter.as_integer()); - auto actual_link_status_transition_counter = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(0, actual_link_status_transition_counter.as_integer()); - auto actual_transmission_status_transition_counter = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(0, actual_transmission_status_transition_counter.as_integer()); - auto actual_external_synchronization_status_transition_counter = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(0, actual_external_synchronization_status_transition_counter.as_integer()); // Check that reset_monitor handler was invoked @@ -756,48 +756,48 @@ BST_TEST_CASE(testActivateDeactivateSenderMonitor) } { // Do deactivation - nmos::deactivate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, gate); + nmos::nc::deactivate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, gate); // Check statuses - auto actual_essence_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_essence_status::status::inactive, actual_essence_status.as_integer()); - auto actual_transmission_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_essence_status::status::inactive, actual_transmission_status.as_integer()); - auto actual_overall_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::inactive, actual_overall_status.as_integer()); } { reset_monitor_called = false; // disable autoResetCounterAndMessages - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_auto_reset_monitor_property_id, web::json::value::boolean(false), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_auto_reset_monitor_property_id, web::json::value::boolean(false), get_control_protocol_class_descriptor, gate); int32_t transition_count = 10; // set transition counters - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); // Do activatation - nmos::activate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, gate); + nmos::nc::activate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, gate); // Check statuses - auto actual_essence_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_essence_status::status::healthy, actual_essence_status.as_integer()); - auto actual_transmission_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_essence_status::status::healthy, actual_transmission_status.as_integer()); - auto actual_overall_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::healthy, actual_overall_status.as_integer()); // Check transition counters - auto actual_essence_status_transition_counter = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transition_count, actual_essence_status_transition_counter.as_integer()); - auto actual_link_status_transition_counter = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transition_count, actual_link_status_transition_counter.as_integer()); - auto actual_transmission_status_transition_counter = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transition_count, actual_transmission_status_transition_counter.as_integer()); - auto actual_external_synchronization_status_transition_counter = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transition_count, actual_external_synchronization_status_transition_counter.as_integer()); // Check that reset_monitor handler was NOT invoked @@ -805,14 +805,14 @@ BST_TEST_CASE(testActivateDeactivateSenderMonitor) } { // Do deactivation - nmos::deactivate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, gate); + nmos::nc::deactivate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, gate); // Check statuses - auto actual_essence_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_essence_status::status::inactive, actual_essence_status.as_integer()); - auto actual_transmission_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_essence_status::status::inactive, actual_transmission_status.as_integer()); - auto actual_overall_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::inactive, actual_overall_status.as_integer()); } } @@ -865,7 +865,7 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) { // Status should change with no delay to Inactive // Initial stream status of healthy - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, 1, gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, 1, gate); // Set stream stream status to inactive at t=0 bool success = nmos::nc::details::set_monitor_status_with_delay(resources, monitor_oid, 0, U(""), @@ -881,7 +881,7 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) gate); BST_REQUIRE(success); - const auto actual_value = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); + const auto actual_value = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); // Expected status to change with no delay to inactive BST_CHECK_EQUAL(0, actual_value.as_integer()); } @@ -889,8 +889,8 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) // Status should change to Inactive with no delay // Initial stream status of healthy and monitor activation time to t=0 - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, 1, gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, 1, gate); long long current_time = 1; // Set stream stream status to inactive at t=1 - within initial status_reporting_delay period @@ -908,11 +908,11 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) BST_REQUIRE(success); // Expected status to change with no delay to inactive - const auto actual_value = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); + const auto actual_value = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); BST_CHECK_EQUAL(0, actual_value.as_integer()); // Expected status pending received time to be 0 i.e. not pending - const auto actual_time = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); + const auto actual_time = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); BST_CHECK_EQUAL(0, actual_time.as_integer()); } { @@ -920,8 +920,8 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) // Any unhealthy status updates should be pending // // Initial stream status of healthy and monitor activation time to t=0 - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, 1, gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, 1, gate); long long current_time = 1; int expected_status = 3; @@ -939,22 +939,22 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) gate); BST_REQUIRE(success); - const auto actual_value = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); + const auto actual_value = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); // Expected status to remain healthy BST_CHECK_EQUAL(1, actual_value.as_integer()); // Expected status pending received time to be current time i.e. pending - const auto actual_time = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); + const auto actual_time = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); BST_CHECK_EQUAL(current_time, actual_time.as_integer()); // Expected status pending to be expected status i.e. pending - const auto actual_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending, gate); + const auto actual_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending, gate); BST_CHECK_EQUAL(expected_status, actual_status.as_integer()); } { // Status updates from healthy to unhealthy after initial reporting delay should happen with no delay // // Initial stream status of healthy and monitor activation time to t=0 - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, 1, gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, 1, gate); long long current_time = 5; int expected_status = 3; @@ -972,19 +972,19 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) gate); BST_REQUIRE(success); - const auto actual_value = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); + const auto actual_value = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); // Expected status to be unhealthy BST_CHECK_EQUAL(expected_status, actual_value.as_integer()); // Expected status pending received time to be 0 i.e. not pending - const auto actual_time = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); + const auto actual_time = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); BST_CHECK_EQUAL(0, actual_time.as_integer()); } { // Status updates from partailly unhealthy to unhealthy after initial reporting delay should happen with no delay // // Initial stream status of partially unhealthy and monitor activation time to t=0 - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, 2, gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, 2, gate); long long current_time = 5; int expected_status = 3; @@ -1002,11 +1002,11 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) gate); BST_REQUIRE(success); - const auto actual_value = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); + const auto actual_value = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); // Expected status to go to unhealthy without delay BST_CHECK_EQUAL(expected_status, actual_value.as_integer()); // Expected status pending received time to be 0 i.e. not pending - const auto actual_time = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); + const auto actual_time = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); BST_CHECK_EQUAL(0, actual_time.as_integer()); } { @@ -1014,8 +1014,8 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) // // Initial stream status of partially unhealthy and monitor activation time to t=0 int initial_status = 2; - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, initial_status, gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, initial_status, gate); long long current_time = 5; int expected_status = 1; @@ -1033,14 +1033,14 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) gate); BST_REQUIRE(success); - const auto actual_value = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); + const auto actual_value = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); // Expected status to stay partially unhealthy BST_CHECK_EQUAL(initial_status, actual_value.as_integer()); // Expected status pending received time to be current time i.e. pending - const auto actual_time = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); + const auto actual_time = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); BST_CHECK_EQUAL(current_time, actual_time.as_integer()); // Expected status pending to be expected status i.e. healthy - const auto actual_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending, gate); + const auto actual_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending, gate); BST_CHECK_EQUAL(expected_status, actual_status.as_integer()); } { @@ -1048,15 +1048,15 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) // // Initial stream status of partially unhealthy and monitor activation time to t=0 int initial_status = 2; - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, initial_status, gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, initial_status, gate); long long current_time = 9; int expected_status = 1; long long received_time = 8; // Status already pending with healthy state at t=8 - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending, expected_status, gate); - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, received_time, gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending, expected_status, gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, received_time, gate); // Set stream stream status to healthy at t=9 - healthy status already pending bool success = nmos::nc::details::set_monitor_status_with_delay(resources, monitor_oid, expected_status, U(""), @@ -1072,14 +1072,14 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) gate); BST_REQUIRE(success); - const auto actual_value = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); + const auto actual_value = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); // Expected status to stay unhealthy BST_CHECK_EQUAL(initial_status, actual_value.as_integer()); // Expected status pending received time to be the original pending time - const auto actual_time = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); + const auto actual_time = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); BST_CHECK_EQUAL(received_time, actual_time.as_integer()); // Expected status pending to be expected status i.e. healthy - const auto actual_status = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending, gate); + const auto actual_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending, gate); BST_CHECK_EQUAL(expected_status, actual_status.as_integer()); } { @@ -1088,9 +1088,9 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) // Initial stream status of healthy and monitor activation time to t=0 int initial_status = 1; utility::string_t initial_status_message = U("initial status message"); - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, initial_status, gate); - nmos::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_message, web::json::value::string(initial_status_message), gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, initial_status, gate); + nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_message, web::json::value::string(initial_status_message), gate); long long current_time = 9; utility::string_t updated_status_message = U("updated status message"); @@ -1109,10 +1109,10 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) gate); BST_REQUIRE(success); - const auto actual_value = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); + const auto actual_value = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); // Expected status to stay healthy BST_CHECK_EQUAL(initial_status, actual_value.as_integer()); - const auto actual_status_message = nmos::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_message, gate); + const auto actual_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_message, gate); // Expected message to have been updated BST_CHECK_EQUAL(updated_status_message, actual_status_message.as_string()); } From c46a46f507f4147d1c24721ea52ad4e443314fd4 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 12 Sep 2025 10:55:49 +0100 Subject: [PATCH 241/250] Rename utils functions to get rid of "nc" or "control_protocol" in names --- .../nmos/control_protocol_behaviour.cpp | 16 +- .../nmos/control_protocol_handlers.cpp | 6 +- Development/nmos/control_protocol_methods.cpp | 4 +- Development/nmos/control_protocol_utils.cpp | 102 ++--- Development/nmos/control_protocol_utils.h | 12 +- .../nmos/test/control_protocol_utils_test.cpp | 412 +++++++++--------- 6 files changed, 276 insertions(+), 276 deletions(-) diff --git a/Development/nmos/control_protocol_behaviour.cpp b/Development/nmos/control_protocol_behaviour.cpp index b31eea8da..49a3483bf 100644 --- a/Development/nmos/control_protocol_behaviour.cpp +++ b/Development/nmos/control_protocol_behaviour.cpp @@ -89,13 +89,13 @@ namespace nmos auto oid = nmos::fields::nc::oid(descriptor); const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); - auto status_reporting_delay = nc::get_control_protocol_property(control_protocol_resources, oid, nc_status_monitor_status_reporting_delay, get_control_protocol_class_descriptor, gate); + auto status_reporting_delay = nc::get_property(control_protocol_resources, oid, nc_status_monitor_status_reporting_delay, get_control_protocol_class_descriptor, gate); - const auto& domain_statuses = nmos::nc::is_nc_sender_monitor(class_id) ? sender_monitor_domain_statuses : receiver_monitor_domain_statuses; + const auto& domain_statuses = nmos::nc::is_sender_monitor(class_id) ? sender_monitor_domain_statuses : receiver_monitor_domain_statuses; for (const auto& domain_status : domain_statuses) { - auto received_time = nc::get_control_protocol_property(control_protocol_resources, oid, domain_status.status_pending_received_time_field_name, gate); + auto received_time = nc::get_property(control_protocol_resources, oid, domain_status.status_pending_received_time_field_name, gate); if (received_time.as_integer() > 0) { @@ -126,13 +126,13 @@ namespace nmos const auto& oid = nmos::fields::nc::oid(descriptor); const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); - const auto status_reporting_delay = nc::get_control_protocol_property(control_protocol_resources, oid, nc_status_monitor_status_reporting_delay, get_control_protocol_class_descriptor, gate); + const auto status_reporting_delay = nc::get_property(control_protocol_resources, oid, nc_status_monitor_status_reporting_delay, get_control_protocol_class_descriptor, gate); - const auto& domain_statuses = nmos::nc::is_nc_sender_monitor(class_id) ? sender_monitor_domain_statuses : receiver_monitor_domain_statuses; + const auto& domain_statuses = nmos::nc::is_sender_monitor(class_id) ? sender_monitor_domain_statuses : receiver_monitor_domain_statuses; for (const auto& domain_status : domain_statuses) { - const auto received_time = nc::get_control_protocol_property(control_protocol_resources, oid, domain_status.status_pending_received_time_field_name, gate); + const auto received_time = nc::get_property(control_protocol_resources, oid, domain_status.status_pending_received_time_field_name, gate); if (received_time.as_integer() > 0) { @@ -141,8 +141,8 @@ namespace nmos if (current_time >= threshold_time) { // copy pending status to status property - const auto& status = nc::get_control_protocol_property(control_protocol_resources, oid, domain_status.status_pending_field_name, gate); - const auto& status_message = nc::get_control_protocol_property(control_protocol_resources, oid, domain_status.status_message_pending_field_name, gate); + const auto& status = nc::get_property(control_protocol_resources, oid, domain_status.status_pending_field_name, gate); + const auto& status_message = nc::get_property(control_protocol_resources, oid, domain_status.status_message_pending_field_name, gate); const auto& status_message_string = status_message == web::json::value::null() ? U("") : status_message.as_string(); nc::details::set_monitor_status(control_protocol_resources, oid, status.as_integer(), status_message_string, domain_status.status_property_id, diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index bee73ed61..f93a59752 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -92,7 +92,7 @@ namespace nmos const bool active = nmos::fields::master_enable(endpoint_active); auto found = nc::find_resource(resources, nmos::types::nc_status_monitor, connection_resource.id); - if (resources.end() != found && nmos::nc::is_nc_status_monitor(details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) + if (resources.end() != found && nmos::nc::is_status_monitor(details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) { const auto& oid = nmos::fields::nc::oid(found->data); @@ -116,7 +116,7 @@ namespace nmos return [&resources, get_control_protocol_class_descriptor, &gate](nc_oid oid, const nc_property_id& property_id) { - return nc::get_control_protocol_property(resources, oid, property_id, get_control_protocol_class_descriptor, gate); + return nc::get_property(resources, oid, property_id, get_control_protocol_class_descriptor, gate); }; } @@ -126,7 +126,7 @@ namespace nmos return [&resources, get_control_protocol_class_descriptor, &gate](nc_oid oid, const nc_property_id& property_id, const web::json::value& value) { - return nc::set_control_protocol_property_and_notify(resources, oid, property_id, value, get_control_protocol_class_descriptor, gate); + return nc::set_property_and_notify(resources, oid, property_id, value, get_control_protocol_class_descriptor, gate); }; } diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index 0c1080c76..e6e4bf26f 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -69,7 +69,7 @@ namespace nmos // Special case for BCP-008-01/02 where it specifies that status monitors cannot be disabled if (nmos::fields::nc::name(property).c_str() == nmos::fields::nc::enabled.key - && nc::is_nc_status_monitor(details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data))) + && nc::is_status_monitor(details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data))) && !val.as_bool()) { utility::ostringstream_t ss; @@ -750,7 +750,7 @@ namespace nmos const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); // reset all counters - const std::vector> property_values = nmos::nc::is_nc_sender_monitor(class_id) ? sender_property_values : receiver_property_values; + const std::vector> property_values = nmos::nc::is_sender_monitor(class_id) ? sender_property_values : receiver_property_values; for (const auto& property_value : property_values) { diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 546687e48..aa4348f23 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -395,26 +395,26 @@ namespace nmos get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { - auto current_connection_status = get_control_protocol_property(resources, oid, status_property_id, get_control_protocol_class_descriptor, gate); + auto current_connection_status = get_property(resources, oid, status_property_id, get_control_protocol_class_descriptor, gate); web::json::value json_status_message = status_message.size() ? web::json::value::string(status_message) : web::json::value::null(); - set_control_protocol_property_and_notify(resources, oid, status_property_id, status, get_control_protocol_class_descriptor, gate); - set_control_protocol_property_and_notify(resources, oid, status_message_property_id, json_status_message, get_control_protocol_class_descriptor, gate); + set_property_and_notify(resources, oid, status_property_id, status, get_control_protocol_class_descriptor, gate); + set_property_and_notify(resources, oid, status_message_property_id, json_status_message, get_control_protocol_class_descriptor, gate); // Cancel any pending status updates - set_control_protocol_property(resources, oid, status_pending_received_time_field_name, web::json::value::number(0), gate); + set_property(resources, oid, status_pending_received_time_field_name, web::json::value::number(0), gate); // if status is "partially unhealthy" (2) or "unhealthy" (3) and less healthy than current state if (status > 1 && status > current_connection_status.as_integer()) { // increment transition_counter - auto transition_counter = get_control_protocol_property(resources, oid, status_transition_counter_property_id, get_control_protocol_class_descriptor, gate).as_integer(); - set_control_protocol_property_and_notify(resources, oid, status_transition_counter_property_id, ++transition_counter, get_control_protocol_class_descriptor, gate); + auto transition_counter = get_property(resources, oid, status_transition_counter_property_id, get_control_protocol_class_descriptor, gate).as_integer(); + set_property_and_notify(resources, oid, status_transition_counter_property_id, ++transition_counter, get_control_protocol_class_descriptor, gate); } const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != found) { - if (nmos::nc::is_nc_sender_monitor(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) + if (nmos::nc::is_sender_monitor(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) { return details::update_sender_monitor_overall_status(resources, oid, get_control_protocol_class_descriptor, gate); } @@ -436,7 +436,7 @@ namespace nmos get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { - const auto& current_status = get_control_protocol_property(resources, oid, status_property_id, get_control_protocol_class_descriptor, gate); + const auto& current_status = get_property(resources, oid, status_property_id, get_control_protocol_class_descriptor, gate); if (current_status.is_null()) { // should never happen, missing receiver/sender monitor status property @@ -448,12 +448,12 @@ namespace nmos if (status == current_status.as_integer()) { // has status message changed? - const auto& current_status_message = get_control_protocol_property(resources, oid, status_message_property_id, get_control_protocol_class_descriptor, gate); + const auto& current_status_message = get_property(resources, oid, status_message_property_id, get_control_protocol_class_descriptor, gate); if ((current_status_message.is_null() && status_message.size()) || (!current_status_message.is_null() && current_status_message.as_string() != status_message)) { // If the status message has changed then update only that web::json::value json_status_message = status_message.size() ? web::json::value::string(status_message) : web::json::value::null(); - return set_control_protocol_property_and_notify(resources, oid, status_message_property_id, json_status_message, get_control_protocol_class_descriptor, gate); + return set_property_and_notify(resources, oid, status_message_property_id, json_status_message, get_control_protocol_class_descriptor, gate); } // no update required, no changes on status or status message return true; @@ -467,7 +467,7 @@ namespace nmos if (1 == status && status_message.size() == 0) { // Get existing status message and prepend with "Previously: " - const auto& current_status_message = get_control_protocol_property(resources, oid, status_message_property_id, get_control_protocol_class_descriptor, gate); + const auto& current_status_message = get_property(resources, oid, status_message_property_id, get_control_protocol_class_descriptor, gate); if (!current_status_message.is_null()) { @@ -483,8 +483,8 @@ namespace nmos } else { - const auto& activation_time = get_control_protocol_property(resources, oid, nmos::fields::nc::monitor_activation_time, gate); - const auto& status_reporting_delay = get_control_protocol_property(resources, oid, nc_status_monitor_status_reporting_delay, get_control_protocol_class_descriptor, gate); + const auto& activation_time = get_property(resources, oid, nmos::fields::nc::monitor_activation_time, gate); + const auto& status_reporting_delay = get_property(resources, oid, nc_status_monitor_status_reporting_delay, get_control_protocol_class_descriptor, gate); if (status > current_status.as_integer() && now_time > (static_cast(activation_time.as_integer()) + status_reporting_delay.as_integer())) { @@ -497,19 +497,19 @@ namespace nmos web::json::value json_status_message = updated_status_message.size() ? web::json::value::string(updated_status_message) : web::json::value::null(); // becoming more health or in the initial activation state // set the status with delay - const auto& pending_received_time = get_control_protocol_property(resources, oid, status_pending_received_time_field_name, gate); + const auto& pending_received_time = get_property(resources, oid, status_pending_received_time_field_name, gate); // only update pending received time if not already set if (pending_received_time.as_integer() == 0) { - if (!set_control_protocol_property(resources, oid, status_pending_received_time_field_name, now_time, gate)) + if (!set_property(resources, oid, status_pending_received_time_field_name, now_time, gate)) { return false; } } - if (set_control_protocol_property(resources, oid, status_pending_field_name, status, gate) - && set_control_protocol_property(resources, oid, status_message_pending_time_field_name, json_status_message, gate)) + if (set_property(resources, oid, status_pending_field_name, status, gate) + && set_property(resources, oid, status_message_pending_time_field_name, json_status_message, gate)) { monitor_status_pending(); return true; @@ -529,11 +529,11 @@ namespace nmos { for (const auto& property_id_pair : status_property_ids) { - auto status = get_control_protocol_property(resources, oid, property_id_pair.first, get_control_protocol_class_descriptor, gate); + auto status = get_property(resources, oid, property_id_pair.first, get_control_protocol_class_descriptor, gate); if (status.as_integer() != status_health) continue; - auto status_message = get_control_protocol_property(resources, oid, property_id_pair.second, get_control_protocol_class_descriptor, gate); + auto status_message = get_property(resources, oid, property_id_pair.second, get_control_protocol_class_descriptor, gate); if (!status_message.is_null()) { @@ -553,20 +553,20 @@ namespace nmos std::pair(nc_receiver_monitor_link_status_property_id, nc_receiver_monitor_link_status_message_property_id)}; // Update Overall Status - auto connection_status = get_control_protocol_property(resources, oid, nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); - auto stream_status = get_control_protocol_property(resources, oid, nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); + auto connection_status = get_property(resources, oid, nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); + auto stream_status = get_property(resources, oid, nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); const auto& overall_status_message = get_overall_status_message(resources, oid, status_message_property_ids, get_control_protocol_class_descriptor, gate); // if connection or stream status is Inactive if (nc_connection_status::status::inactive == connection_status.as_integer() || nc_stream_status::status::inactive == stream_status.as_integer()) { // Overall status is set to Inactive - bool success = set_control_protocol_property_and_notify(resources, oid, nc_status_monitor_overall_status_property_id, nc_overall_status::status::inactive, get_control_protocol_class_descriptor, gate); - return success && set_control_protocol_property_and_notify(resources, oid, nc_status_monitor_overall_status_message_property_id, overall_status_message, get_control_protocol_class_descriptor, gate); + bool success = set_property_and_notify(resources, oid, nc_status_monitor_overall_status_property_id, nc_overall_status::status::inactive, get_control_protocol_class_descriptor, gate); + return success && set_property_and_notify(resources, oid, nc_status_monitor_overall_status_message_property_id, overall_status_message, get_control_protocol_class_descriptor, gate); } - auto link_status = get_control_protocol_property(resources, oid, nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); - auto external_synchronization_status = get_control_protocol_property(resources, oid, nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto link_status = get_property(resources, oid, nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto external_synchronization_status = get_property(resources, oid, nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); // otherwise take the least healthy status as the overall status std::vector statuses = {link_status.as_integer(), connection_status.as_integer(), stream_status.as_integer()}; @@ -578,8 +578,8 @@ namespace nmos } // Find most unhealthy status auto overall_status = *std::max_element(statuses.begin(), statuses.end()); - bool success = set_control_protocol_property_and_notify(resources, oid, nc_status_monitor_overall_status_property_id, web::json::value::number(overall_status), get_control_protocol_class_descriptor, gate); - return success && set_control_protocol_property_and_notify(resources, oid, nc_status_monitor_overall_status_message_property_id, overall_status_message, get_control_protocol_class_descriptor, gate); + bool success = set_property_and_notify(resources, oid, nc_status_monitor_overall_status_property_id, web::json::value::number(overall_status), get_control_protocol_class_descriptor, gate); + return success && set_property_and_notify(resources, oid, nc_status_monitor_overall_status_message_property_id, overall_status_message, get_control_protocol_class_descriptor, gate); } bool update_sender_monitor_overall_status(resources& resources, nc_oid oid, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) @@ -591,8 +591,8 @@ namespace nmos std::pair(nc_sender_monitor_link_status_property_id, nc_sender_monitor_link_status_message_property_id)}; // Update Overall Status - auto transmission_status = get_control_protocol_property(resources, oid, nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); - auto essence_status = get_control_protocol_property(resources, oid, nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); + auto transmission_status = get_property(resources, oid, nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); + auto essence_status = get_property(resources, oid, nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); const auto& overall_status_message = get_overall_status_message(resources, oid, status_message_property_ids, get_control_protocol_class_descriptor, gate); @@ -600,12 +600,12 @@ namespace nmos if (nc_transmission_status::status::inactive == transmission_status.as_integer() || nc_essence_status::status::inactive == essence_status.as_integer()) { // Overall status is set to Inactive - bool success = set_control_protocol_property_and_notify(resources, oid, nc_status_monitor_overall_status_property_id, nc_overall_status::status::inactive, get_control_protocol_class_descriptor, gate); - return success && set_control_protocol_property_and_notify(resources, oid, nc_status_monitor_overall_status_message_property_id, overall_status_message, get_control_protocol_class_descriptor, gate); + bool success = set_property_and_notify(resources, oid, nc_status_monitor_overall_status_property_id, nc_overall_status::status::inactive, get_control_protocol_class_descriptor, gate); + return success && set_property_and_notify(resources, oid, nc_status_monitor_overall_status_message_property_id, overall_status_message, get_control_protocol_class_descriptor, gate); } - auto link_status = get_control_protocol_property(resources, oid, nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); - auto external_synchronization_status = get_control_protocol_property(resources, oid, nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto link_status = get_property(resources, oid, nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto external_synchronization_status = get_property(resources, oid, nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); // otherwise take the least healthy status as the overall status std::vector statuses = {link_status.as_integer(), transmission_status.as_integer(), essence_status.as_integer()}; @@ -617,8 +617,8 @@ namespace nmos } // Find most unhealthy status auto overall_status = *std::max_element(statuses.begin(), statuses.end()); - bool success = set_control_protocol_property_and_notify(resources, oid, nc_status_monitor_overall_status_property_id, web::json::value::number(overall_status), get_control_protocol_class_descriptor, gate); - return success && set_control_protocol_property_and_notify(resources, oid, nc_status_monitor_overall_status_message_property_id, overall_status_message, get_control_protocol_class_descriptor, gate); + bool success = set_property_and_notify(resources, oid, nc_status_monitor_overall_status_property_id, web::json::value::number(overall_status), get_control_protocol_class_descriptor, gate); + return success && set_property_and_notify(resources, oid, nc_status_monitor_overall_status_message_property_id, overall_status_message, get_control_protocol_class_descriptor, gate); } } @@ -653,13 +653,13 @@ namespace nmos } // is the given class_id a NcStatusMonitor - bool is_nc_status_monitor(const nc_class_id& class_id) + bool is_status_monitor(const nc_class_id& class_id) { return details::is_control_class(nc_status_monitor_class_id, class_id); } // is the given class_id a NcStatusMonitor - bool is_nc_sender_monitor(const nc_class_id& class_id) + bool is_sender_monitor(const nc_class_id& class_id) { return details::is_control_class(nc_sender_monitor_class_id, class_id); } @@ -1088,7 +1088,7 @@ namespace nmos } } - web::json::value get_control_protocol_property(const resources& resources, nc_oid oid, const nc_property_id& property_id, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + web::json::value get_property(const resources& resources, nc_oid oid, const nc_property_id& property_id, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { // get resource based on the oid const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); @@ -1106,7 +1106,7 @@ namespace nmos return web::json::value::null(); } - bool set_control_protocol_property_and_notify(resources& resources, nc_oid oid, const nc_property_id& property_id, const web::json::value& value, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + bool set_property_and_notify(resources& resources, nc_oid oid, const nc_property_id& property_id, const web::json::value& value, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != found) @@ -1142,7 +1142,7 @@ namespace nmos return false; } - bool set_control_protocol_property(resources& resources, nc_oid oid, const utility::string_t& property_name, const web::json::value& value, slog::base_gate& gate) + bool set_property(resources& resources, nc_oid oid, const utility::string_t& property_name, const web::json::value& value, slog::base_gate& gate) { try { @@ -1159,7 +1159,7 @@ namespace nmos } } - web::json::value get_control_protocol_property(const resources& resources, nc_oid oid, const utility::string_t& property_name, slog::base_gate& gate) + web::json::value get_property(const resources& resources, nc_oid oid, const utility::string_t& property_name, slog::base_gate& gate) { // get resource based on the oid const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); @@ -1292,7 +1292,7 @@ namespace nmos bool set_monitor_synchronization_source_id(resources& resources, nc_oid oid, const bst::optional& source_id_, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { web::json::value source_id = source_id_ ? web::json::value::string(*source_id_) : web::json::value{}; - return set_control_protocol_property_and_notify(resources, oid, nc_receiver_monitor_synchronization_source_id_property_id, source_id, get_control_protocol_class_descriptor, gate); + return set_property_and_notify(resources, oid, nc_receiver_monitor_synchronization_source_id_property_id, source_id, get_control_protocol_class_descriptor, gate); } bool activate_monitor(resources& resources, nc_oid oid, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, slog::base_gate& gate) @@ -1302,16 +1302,16 @@ namespace nmos // Furthermore, after activation, as long as the monitor isn’t being deactivated, it MUST delay the reporting // of non Healthy states for the duration specified by statusReportingDelay, and then transition to any other appropriate state. const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found && nc::is_nc_status_monitor(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) + if (resources.end() != found && nc::is_status_monitor(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) { const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)); auto activation_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - auto succeed = set_control_protocol_property(resources, oid, nmos::fields::nc::monitor_activation_time, activation_time, gate); + auto succeed = set_property(resources, oid, nmos::fields::nc::monitor_activation_time, activation_time, gate); // If autoResetCountersAndMessages set to true then reset the transition counters bool auto_reset_monitor{false}; - if (nc::is_nc_sender_monitor(class_id)) + if (nc::is_sender_monitor(class_id)) { if (succeed) succeed = set_sender_monitor_transmission_status(resources, oid, nmos::nc_transmission_status::status::healthy, U("Sender activated"), get_control_protocol_class_descriptor, gate); if (succeed) succeed = set_sender_monitor_essence_status(resources, oid, nmos::nc_essence_status::status::healthy, U("Sender activated"), get_control_protocol_class_descriptor, gate); @@ -1324,15 +1324,15 @@ namespace nmos if (succeed) { - auto auto_reset_property_id = nc::is_nc_sender_monitor(class_id) ? nmos::nc_sender_monitor_auto_reset_monitor_property_id : nmos::nc_receiver_monitor_auto_reset_monitor_property_id; - auto auto_reset_monitor_ = get_control_protocol_property(resources, oid, auto_reset_property_id, get_control_protocol_class_descriptor, gate); + auto auto_reset_property_id = nc::is_sender_monitor(class_id) ? nmos::nc_sender_monitor_auto_reset_monitor_property_id : nmos::nc_receiver_monitor_auto_reset_monitor_property_id; + auto auto_reset_monitor_ = get_property(resources, oid, auto_reset_property_id, get_control_protocol_class_descriptor, gate); if (auto_reset_monitor_.is_null()) succeed = false; else auto_reset_monitor = auto_reset_monitor_.as_bool(); } if (succeed && auto_reset_monitor) { - auto reset_monitor_method_id = nc::is_nc_sender_monitor(class_id) ? nmos::nc_sender_monitor_reset_monitor_method_id : nmos::nc_receiver_monitor_reset_monitor_method_id; + auto reset_monitor_method_id = nc::is_sender_monitor(class_id) ? nmos::nc_sender_monitor_reset_monitor_method_id : nmos::nc_receiver_monitor_reset_monitor_method_id; // find the method_handler for the reset monitor method auto method = get_control_protocol_method_descriptor(class_id, reset_monitor_method_id); auto& nc_method_descriptor = method.first; @@ -1361,13 +1361,13 @@ namespace nmos bool deactivate_monitor(resources& resources, nc_oid oid, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found && nc::is_nc_status_monitor(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) + if (resources.end() != found && nc::is_status_monitor(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) { const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)); - auto succeed = set_control_protocol_property(resources, oid, nmos::fields::nc::monitor_activation_time, web::json::value::number(0), gate); + auto succeed = set_property(resources, oid, nmos::fields::nc::monitor_activation_time, web::json::value::number(0), gate); - if (nc::is_nc_sender_monitor(class_id)) + if (nc::is_sender_monitor(class_id)) { if (succeed) succeed = set_sender_monitor_transmission_status(resources, oid, nmos::nc_transmission_status::status::inactive, U("Sender deactivated"), get_control_protocol_class_descriptor, gate); if (succeed) succeed = set_sender_monitor_essence_status(resources, oid, nmos::nc_essence_status::status::inactive, U("Sender deactivated"), get_control_protocol_class_descriptor, gate); diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index 559c45c4f..8e3a683d7 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -78,10 +78,10 @@ namespace nmos bool is_class_manager(const nc_class_id& class_id); // is the given class_id a NcStatusMonitor - bool is_nc_status_monitor(const nc_class_id& class_id); + bool is_status_monitor(const nc_class_id& class_id); // is the given class_id a NcSenderMonitor - bool is_nc_sender_monitor(const nc_class_id& class_id); + bool is_sender_monitor(const nc_class_id& class_id); // construct NcClassId nc_class_id make_class_id(const nc_class_id& prefix, int32_t authority_key, const std::vector& suffix); @@ -132,13 +132,13 @@ namespace nmos void insert_notification_events(resources& resources, const api_version& version, const api_version& downgrade_version, const type& type, const web::json::value& pre, const web::json::value& post, const web::json::value& event); // get property value given oid and property_id - web::json::value get_control_protocol_property(const resources& resources, nc_oid oid, const nc_property_id& property_id, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + web::json::value get_property(const resources& resources, nc_oid oid, const nc_property_id& property_id, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); // set property value given oid and property_id and notify - bool set_control_protocol_property_and_notify(resources& resources, nc_oid oid, const nc_property_id& property_id, const web::json::value& value, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + bool set_property_and_notify(resources& resources, nc_oid oid, const nc_property_id& property_id, const web::json::value& value, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); // set hidden property but don't notify, as property isn't part of class definition - bool set_control_protocol_property(resources& resources, nc_oid oid, const utility::string_t& property_name, const web::json::value& value, slog::base_gate& gate); + bool set_property(resources& resources, nc_oid oid, const utility::string_t& property_name, const web::json::value& value, slog::base_gate& gate); // Set link status and link status message bool set_receiver_monitor_link_status(resources& resources, nc_oid oid, nmos::nc_link_status::status link_status, const utility::string_t& link_status_message, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); @@ -189,7 +189,7 @@ namespace nmos bool set_sender_monitor_essence_status_with_delay(resources& resources, nc_oid oid, nmos::nc_essence_status::status essence_status, const utility::string_t& essence_status_message, monitor_status_pending_handler monitor_status_pending, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); // Get property by name, rather than by property id. Used to get "hidden" resource properties - web::json::value get_control_protocol_property(const resources& resources, nc_oid oid, const utility::string_t& property_name, slog::base_gate& gate); + web::json::value get_property(const resources& resources, nc_oid oid, const utility::string_t& property_name, slog::base_gate& gate); } } diff --git a/Development/nmos/test/control_protocol_utils_test.cpp b/Development/nmos/test/control_protocol_utils_test.cpp index d66e4bbeb..8687db947 100644 --- a/Development/nmos/test/control_protocol_utils_test.cpp +++ b/Development/nmos/test/control_protocol_utils_test.cpp @@ -36,21 +36,21 @@ BST_TEST_CASE(testGetSetControlProtocolProperty) insert_resource(resources, std::move(root_block)); insert_resource(resources, std::move(class_manager)); - auto oid = nmos::nc::get_control_protocol_property(resources, class_manager_oid, nmos::nc_object_oid_property_id, get_control_protocol_class_descriptor, gate); + auto oid = nmos::nc::get_property(resources, class_manager_oid, nmos::nc_object_oid_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(class_manager_oid, static_cast(oid.as_integer())); - auto role = nmos::nc::get_control_protocol_property(resources, class_manager_oid, nmos::nc_object_role_property_id, get_control_protocol_class_descriptor, gate); + auto role = nmos::nc::get_property(resources, class_manager_oid, nmos::nc_object_role_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(U("ClassManager"), role.as_string()); auto test_label = U("ThisIsATest"); - bool result = nmos::nc::set_control_protocol_property_and_notify(resources, class_manager_oid, nmos::nc_object_user_label_property_id, web::json::value::string(test_label), get_control_protocol_class_descriptor, gate); + bool result = nmos::nc::set_property_and_notify(resources, class_manager_oid, nmos::nc_object_user_label_property_id, web::json::value::string(test_label), get_control_protocol_class_descriptor, gate); BST_REQUIRE(result); - auto label = nmos::nc::get_control_protocol_property(resources, class_manager_oid, nmos::nc_object_user_label_property_id, get_control_protocol_class_descriptor, gate); + auto label = nmos::nc::get_property(resources, class_manager_oid, nmos::nc_object_user_label_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(test_label, label.as_string()); } @@ -103,32 +103,32 @@ BST_TEST_CASE(testSetReceiverMonitorStatuses) BST_REQUIRE(nmos::nc::set_receiver_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); BST_REQUIRE(nmos::nc::set_receiver_monitor_stream_status(resources, monitor_oid, stream_status, stream_status_message, get_control_protocol_class_descriptor, gate)); - auto actual_link_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status, actual_link_status.as_integer()); - auto actual_link_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status_message, actual_link_status_message.as_string()); - auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_link_status_transition_counter, actual_link_status_transition_counter.as_integer()); - auto actual_connection_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(connection_status, actual_connection_status.as_integer()); - auto actual_connection_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(connection_status_message, actual_connection_status_message.as_string()); - auto actual_connection_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_connection_status_transition_counter, actual_connection_status_transition_counter.as_integer()); - auto actual_external_synchronization_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status, actual_external_synchronization_status.as_integer()); - auto actual_external_synchronization_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status_message, actual_external_synchronization_status_message.as_string()); - auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_external_synchronization_status_transition_counter, actual_external_synchronization_status_transition_counter.as_integer()); - auto actual_stream_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(stream_status, actual_stream_status.as_integer()); - auto actual_stream_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(stream_status_message, actual_stream_status_message.as_string()); - auto actual_stream_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_stream_status_transition_counter, actual_stream_status_transition_counter.as_integer()); - auto overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto overall_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::inactive, overall_status.as_integer()); } { @@ -150,32 +150,32 @@ BST_TEST_CASE(testSetReceiverMonitorStatuses) BST_REQUIRE(nmos::nc::set_receiver_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); BST_REQUIRE(nmos::nc::set_receiver_monitor_stream_status(resources, monitor_oid, stream_status, stream_status_message, get_control_protocol_class_descriptor, gate)); - auto actual_link_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status, actual_link_status.as_integer()); - auto actual_link_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status_message, actual_link_status_message.as_string()); - auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_link_status_transition_counter, actual_link_status_transition_counter.as_integer()); - auto actual_connection_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(connection_status, actual_connection_status.as_integer()); - auto actual_connection_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(connection_status_message, actual_connection_status_message.as_string()); - auto actual_connection_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_connection_status_transition_counter, actual_connection_status_transition_counter.as_integer()); - auto actual_external_synchronization_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status, actual_external_synchronization_status.as_integer()); - auto actual_external_synchronization_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status_message, actual_external_synchronization_status_message.as_string()); - auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_external_synchronization_status_transition_counter, actual_external_synchronization_status_transition_counter.as_integer()); - auto actual_stream_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(stream_status, actual_stream_status.as_integer()); - auto actual_stream_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(stream_status_message, actual_stream_status_message.as_string()); - auto actual_stream_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_stream_status_transition_counter, actual_stream_status_transition_counter.as_integer()); - auto overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto overall_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::healthy, overall_status.as_integer()); } { @@ -197,32 +197,32 @@ BST_TEST_CASE(testSetReceiverMonitorStatuses) BST_REQUIRE(nmos::nc::set_receiver_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); BST_REQUIRE(nmos::nc::set_receiver_monitor_stream_status(resources, monitor_oid, stream_status, stream_status_message, get_control_protocol_class_descriptor, gate)); - auto actual_link_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status, actual_link_status.as_integer()); - auto actual_link_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status_message, actual_link_status_message.as_string()); - auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_link_status_transition_counter, actual_link_status_transition_counter.as_integer()); - auto actual_connection_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(connection_status, actual_connection_status.as_integer()); - auto actual_connection_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(connection_status_message, actual_connection_status_message.as_string()); - auto actual_connection_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_connection_status_transition_counter, actual_connection_status_transition_counter.as_integer()); - auto actual_external_synchronization_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status, actual_external_synchronization_status.as_integer()); - auto actual_external_synchronization_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status_message, actual_external_synchronization_status_message.as_string()); - auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_external_synchronization_status_transition_counter, actual_external_synchronization_status_transition_counter.as_integer()); - auto actual_stream_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(stream_status, actual_stream_status.as_integer()); - auto actual_stream_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(stream_status_message, actual_stream_status_message.as_string()); - auto actual_stream_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_stream_status_transition_counter, actual_stream_status_transition_counter.as_integer()); - auto overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto overall_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::partially_healthy, overall_status.as_integer()); } { @@ -244,32 +244,32 @@ BST_TEST_CASE(testSetReceiverMonitorStatuses) BST_REQUIRE(nmos::nc::set_receiver_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); BST_REQUIRE(nmos::nc::set_receiver_monitor_stream_status(resources, monitor_oid, stream_status, stream_status_message, get_control_protocol_class_descriptor, gate)); - auto actual_link_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status, actual_link_status.as_integer()); - auto actual_link_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status_message, actual_link_status_message.as_string()); - auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_link_status_transition_counter, actual_link_status_transition_counter.as_integer()); - auto actual_connection_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(connection_status, actual_connection_status.as_integer()); - auto actual_connection_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(connection_status_message, actual_connection_status_message.as_string()); - auto actual_connection_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_connection_status_transition_counter, actual_connection_status_transition_counter.as_integer()); - auto actual_external_synchronization_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status, actual_external_synchronization_status.as_integer()); - auto actual_external_synchronization_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status_message, actual_external_synchronization_status_message.as_string()); - auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_external_synchronization_status_transition_counter, actual_external_synchronization_status_transition_counter.as_integer()); - auto actual_stream_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(stream_status, actual_stream_status.as_integer()); - auto actual_stream_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(stream_status_message, actual_stream_status_message.as_string()); - auto actual_stream_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_stream_status_transition_counter, actual_stream_status_transition_counter.as_integer()); - auto overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto overall_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::unhealthy, overall_status.as_integer()); } } @@ -306,12 +306,12 @@ BST_TEST_CASE(testSetSynchronizationSourceId) { utility::string_t sync_source_id = U("SYNCID"); nmos::nc::set_monitor_synchronization_source_id(resources, monitor_oid, sync_source_id, get_control_protocol_class_descriptor, gate); - auto actual_sync_source_id = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_synchronization_source_id_property_id, get_control_protocol_class_descriptor, gate); + auto actual_sync_source_id = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_synchronization_source_id_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(sync_source_id, actual_sync_source_id.as_string()); } { nmos::nc::set_monitor_synchronization_source_id(resources, monitor_oid, {}, get_control_protocol_class_descriptor, gate); - auto actual_sync_source_id = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_synchronization_source_id_property_id, get_control_protocol_class_descriptor, gate); + auto actual_sync_source_id = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_synchronization_source_id_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK(actual_sync_source_id.is_null()); } } @@ -359,34 +359,34 @@ BST_TEST_CASE(testActivateDeactivateReceiverMonitor) { // autoResetCounterAndMessages will reset all counters and messages on activate, including calling back into application code - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_auto_reset_monitor_property_id, web::json::value::boolean(true), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_auto_reset_monitor_property_id, web::json::value::boolean(true), get_control_protocol_class_descriptor, gate); uint32_t transition_count = 10; // set transition counters - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); // Do activatation nmos::nc::activate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, gate); // Check statuses - auto actual_stream_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_stream_status::status::healthy, actual_stream_status.as_integer()); - auto actual_connection_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_stream_status::status::healthy, actual_connection_status.as_integer()); - auto actual_overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_overall_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::healthy, actual_overall_status.as_integer()); // Check transition counters - auto actual_stream_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(0, actual_stream_status_transition_counter.as_integer()); - auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(0, actual_link_status_transition_counter.as_integer()); - auto actual_connection_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(0, actual_connection_status_transition_counter.as_integer()); - auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(0, actual_external_synchronization_status_transition_counter.as_integer()); // Check that reset_monitor handler was invoked @@ -397,45 +397,45 @@ BST_TEST_CASE(testActivateDeactivateReceiverMonitor) nmos::nc::deactivate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, gate); // Check statuses - auto actual_stream_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_stream_status::status::inactive, actual_stream_status.as_integer()); - auto actual_connection_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_stream_status::status::inactive, actual_connection_status.as_integer()); - auto actual_overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_overall_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::inactive, actual_overall_status.as_integer()); } { reset_monitor_called = false; // disable autoResetCounterAndMessages - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_auto_reset_monitor_property_id, web::json::value::boolean(false), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_auto_reset_monitor_property_id, web::json::value::boolean(false), get_control_protocol_class_descriptor, gate); int32_t transition_count = 10; // set transition counters - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); // Do activatation nmos::nc::activate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, gate); // Check statuses - auto actual_stream_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_stream_status::status::healthy, actual_stream_status.as_integer()); - auto actual_connection_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_stream_status::status::healthy, actual_connection_status.as_integer()); - auto actual_overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_overall_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::healthy, actual_overall_status.as_integer()); // Check transition counters - auto actual_stream_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transition_count, actual_stream_status_transition_counter.as_integer()); - auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transition_count, actual_link_status_transition_counter.as_integer()); - auto actual_connection_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transition_count, actual_connection_status_transition_counter.as_integer()); - auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transition_count, actual_external_synchronization_status_transition_counter.as_integer()); // Check that reset_monitor handler was NOT invoked @@ -446,11 +446,11 @@ BST_TEST_CASE(testActivateDeactivateReceiverMonitor) nmos::nc::deactivate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, gate); // Check statuses - auto actual_stream_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_stream_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_stream_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_stream_status::status::inactive, actual_stream_status.as_integer()); - auto actual_connection_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_connection_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_receiver_monitor_connection_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_stream_status::status::inactive, actual_connection_status.as_integer()); - auto actual_overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_overall_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::inactive, actual_overall_status.as_integer()); } } @@ -507,32 +507,32 @@ BST_TEST_CASE(testSetSenderMonitorStatuses) BST_REQUIRE(nmos::nc::set_sender_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); BST_REQUIRE(nmos::nc::set_sender_monitor_essence_status(resources, monitor_oid, essence_status, essence_status_message, get_control_protocol_class_descriptor, gate)); - auto actual_link_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status, actual_link_status.as_integer()); - auto actual_link_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status_message, actual_link_status_message.as_string()); - auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_link_status_transition_counter, actual_link_status_transition_counter.as_integer()); - auto actual_transmission_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transmission_status, actual_transmission_status.as_integer()); - auto actual_transmission_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transmission_status_message, actual_transmission_status_message.as_string()); - auto actual_transmission_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_transmission_status_transition_counter, actual_transmission_status_transition_counter.as_integer()); - auto actual_external_synchronization_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status, actual_external_synchronization_status.as_integer()); - auto actual_external_synchronization_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status_message, actual_external_synchronization_status_message.as_string()); - auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_external_synchronization_status_transition_counter, actual_external_synchronization_status_transition_counter.as_integer()); - auto actual_essence_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(essence_status, actual_essence_status.as_integer()); - auto actual_essence_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(essence_status_message, actual_essence_status_message.as_string()); - auto actual_essence_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_essence_status_transition_counter, actual_essence_status_transition_counter.as_integer()); - auto overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto overall_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::inactive, overall_status.as_integer()); } { @@ -554,32 +554,32 @@ BST_TEST_CASE(testSetSenderMonitorStatuses) BST_REQUIRE(nmos::nc::set_sender_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); BST_REQUIRE(nmos::nc::set_sender_monitor_essence_status(resources, monitor_oid, essence_status, essence_status_message, get_control_protocol_class_descriptor, gate)); - auto actual_link_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status, actual_link_status.as_integer()); - auto actual_link_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status_message, actual_link_status_message.as_string()); - auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_link_status_transition_counter, actual_link_status_transition_counter.as_integer()); - auto actual_transmission_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transmission_status, actual_transmission_status.as_integer()); - auto actual_transmission_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transmission_status_message, actual_transmission_status_message.as_string()); - auto actual_transmission_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_transmission_status_transition_counter, actual_transmission_status_transition_counter.as_integer()); - auto actual_external_synchronization_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status, actual_external_synchronization_status.as_integer()); - auto actual_external_synchronization_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status_message, actual_external_synchronization_status_message.as_string()); - auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_external_synchronization_status_transition_counter, actual_external_synchronization_status_transition_counter.as_integer()); - auto actual_essence_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(essence_status, actual_essence_status.as_integer()); - auto actual_essence_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(essence_status_message, actual_essence_status_message.as_string()); - auto actual_essence_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_essence_status_transition_counter, actual_essence_status_transition_counter.as_integer()); - auto overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto overall_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::healthy, overall_status.as_integer()); } { @@ -601,32 +601,32 @@ BST_TEST_CASE(testSetSenderMonitorStatuses) BST_REQUIRE(nmos::nc::set_sender_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); BST_REQUIRE(nmos::nc::set_sender_monitor_essence_status(resources, monitor_oid, essence_status, essence_status_message, get_control_protocol_class_descriptor, gate)); - auto actual_link_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status, actual_link_status.as_integer()); - auto actual_link_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status_message, actual_link_status_message.as_string()); - auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_link_status_transition_counter, actual_link_status_transition_counter.as_integer()); - auto actual_transmission_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transmission_status, actual_transmission_status.as_integer()); - auto actual_transmission_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transmission_status_message, actual_transmission_status_message.as_string()); - auto actual_transmission_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_transmission_status_transition_counter, actual_transmission_status_transition_counter.as_integer()); - auto actual_external_synchronization_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status, actual_external_synchronization_status.as_integer()); - auto actual_external_synchronization_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status_message, actual_external_synchronization_status_message.as_string()); - auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_external_synchronization_status_transition_counter, actual_external_synchronization_status_transition_counter.as_integer()); - auto actual_essence_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(essence_status, actual_essence_status.as_integer()); - auto actual_essence_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(essence_status_message, actual_essence_status_message.as_string()); - auto actual_essence_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_essence_status_transition_counter, actual_essence_status_transition_counter.as_integer()); - auto overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto overall_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::partially_healthy, overall_status.as_integer()); } { @@ -648,32 +648,32 @@ BST_TEST_CASE(testSetSenderMonitorStatuses) BST_REQUIRE(nmos::nc::set_sender_monitor_external_synchronization_status(resources, monitor_oid, external_synchronization_status, external_synchronization_status_message, get_control_protocol_class_descriptor, gate)); BST_REQUIRE(nmos::nc::set_sender_monitor_essence_status(resources, monitor_oid, essence_status, essence_status_message, get_control_protocol_class_descriptor, gate)); - auto actual_link_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status, actual_link_status.as_integer()); - auto actual_link_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(link_status_message, actual_link_status_message.as_string()); - auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_link_status_transition_counter, actual_link_status_transition_counter.as_integer()); - auto actual_transmission_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transmission_status, actual_transmission_status.as_integer()); - auto actual_transmission_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transmission_status_message, actual_transmission_status_message.as_string()); - auto actual_transmission_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_transmission_status_transition_counter, actual_transmission_status_transition_counter.as_integer()); - auto actual_external_synchronization_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status, actual_external_synchronization_status.as_integer()); - auto actual_external_synchronization_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(external_synchronization_status_message, actual_external_synchronization_status_message.as_string()); - auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_external_synchronization_status_transition_counter, actual_external_synchronization_status_transition_counter.as_integer()); - auto actual_essence_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(essence_status, actual_essence_status.as_integer()); - auto actual_essence_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_message_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_message_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(essence_status_message, actual_essence_status_message.as_string()); - auto actual_essence_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(expected_essence_status_transition_counter, actual_essence_status_transition_counter.as_integer()); - auto overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto overall_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::unhealthy, overall_status.as_integer()); } } @@ -721,34 +721,34 @@ BST_TEST_CASE(testActivateDeactivateSenderMonitor) { // autoResetCounterAndMessages will reset all counters and messages on activate, including calling back into application code - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_auto_reset_monitor_property_id, web::json::value::boolean(true), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_auto_reset_monitor_property_id, web::json::value::boolean(true), get_control_protocol_class_descriptor, gate); uint32_t transition_count = 10; // set transition counters - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); // Do activatation nmos::nc::activate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, gate); // Check statuses - auto actual_essence_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_essence_status::status::healthy, actual_essence_status.as_integer()); - auto actual_transmission_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_essence_status::status::healthy, actual_transmission_status.as_integer()); - auto actual_overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_overall_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::healthy, actual_overall_status.as_integer()); // Check transition counters - auto actual_essence_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(0, actual_essence_status_transition_counter.as_integer()); - auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(0, actual_link_status_transition_counter.as_integer()); - auto actual_transmission_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(0, actual_transmission_status_transition_counter.as_integer()); - auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(0, actual_external_synchronization_status_transition_counter.as_integer()); // Check that reset_monitor handler was invoked @@ -759,45 +759,45 @@ BST_TEST_CASE(testActivateDeactivateSenderMonitor) nmos::nc::deactivate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, gate); // Check statuses - auto actual_essence_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_essence_status::status::inactive, actual_essence_status.as_integer()); - auto actual_transmission_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_essence_status::status::inactive, actual_transmission_status.as_integer()); - auto actual_overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_overall_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::inactive, actual_overall_status.as_integer()); } { reset_monitor_called = false; // disable autoResetCounterAndMessages - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_auto_reset_monitor_property_id, web::json::value::boolean(false), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_auto_reset_monitor_property_id, web::json::value::boolean(false), get_control_protocol_class_descriptor, gate); int32_t transition_count = 10; // set transition counters - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); - nmos::nc::set_control_protocol_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); + nmos::nc::set_property_and_notify(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, web::json::value::number(transition_count), get_control_protocol_class_descriptor, gate); // Do activatation nmos::nc::activate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, get_control_protocol_method_descriptor, gate); // Check statuses - auto actual_essence_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_essence_status::status::healthy, actual_essence_status.as_integer()); - auto actual_transmission_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_essence_status::status::healthy, actual_transmission_status.as_integer()); - auto actual_overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_overall_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::healthy, actual_overall_status.as_integer()); // Check transition counters - auto actual_essence_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transition_count, actual_essence_status_transition_counter.as_integer()); - auto actual_link_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_link_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_link_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transition_count, actual_link_status_transition_counter.as_integer()); - auto actual_transmission_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transition_count, actual_transmission_status_transition_counter.as_integer()); - auto actual_external_synchronization_status_transition_counter = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); + auto actual_external_synchronization_status_transition_counter = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_external_synchronization_status_transition_counter_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(transition_count, actual_external_synchronization_status_transition_counter.as_integer()); // Check that reset_monitor handler was NOT invoked @@ -808,11 +808,11 @@ BST_TEST_CASE(testActivateDeactivateSenderMonitor) nmos::nc::deactivate_monitor(resources, monitor_oid, get_control_protocol_class_descriptor, gate); // Check statuses - auto actual_essence_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_essence_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_essence_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_essence_status::status::inactive, actual_essence_status.as_integer()); - auto actual_transmission_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_transmission_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_sender_monitor_transmission_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_essence_status::status::inactive, actual_transmission_status.as_integer()); - auto actual_overall_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); + auto actual_overall_status = nmos::nc::get_property(resources, monitor_oid, nmos::nc_status_monitor_overall_status_property_id, get_control_protocol_class_descriptor, gate); BST_CHECK_EQUAL(nmos::nc_overall_status::status::inactive, actual_overall_status.as_integer()); } } @@ -865,7 +865,7 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) { // Status should change with no delay to Inactive // Initial stream status of healthy - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, 1, gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::stream_status, 1, gate); // Set stream stream status to inactive at t=0 bool success = nmos::nc::details::set_monitor_status_with_delay(resources, monitor_oid, 0, U(""), @@ -881,7 +881,7 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) gate); BST_REQUIRE(success); - const auto actual_value = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); + const auto actual_value = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); // Expected status to change with no delay to inactive BST_CHECK_EQUAL(0, actual_value.as_integer()); } @@ -889,8 +889,8 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) // Status should change to Inactive with no delay // Initial stream status of healthy and monitor activation time to t=0 - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, 1, gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::stream_status, 1, gate); long long current_time = 1; // Set stream stream status to inactive at t=1 - within initial status_reporting_delay period @@ -908,11 +908,11 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) BST_REQUIRE(success); // Expected status to change with no delay to inactive - const auto actual_value = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); + const auto actual_value = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); BST_CHECK_EQUAL(0, actual_value.as_integer()); // Expected status pending received time to be 0 i.e. not pending - const auto actual_time = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); + const auto actual_time = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); BST_CHECK_EQUAL(0, actual_time.as_integer()); } { @@ -920,8 +920,8 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) // Any unhealthy status updates should be pending // // Initial stream status of healthy and monitor activation time to t=0 - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, 1, gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::stream_status, 1, gate); long long current_time = 1; int expected_status = 3; @@ -939,22 +939,22 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) gate); BST_REQUIRE(success); - const auto actual_value = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); + const auto actual_value = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); // Expected status to remain healthy BST_CHECK_EQUAL(1, actual_value.as_integer()); // Expected status pending received time to be current time i.e. pending - const auto actual_time = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); + const auto actual_time = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); BST_CHECK_EQUAL(current_time, actual_time.as_integer()); // Expected status pending to be expected status i.e. pending - const auto actual_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending, gate); + const auto actual_status = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending, gate); BST_CHECK_EQUAL(expected_status, actual_status.as_integer()); } { // Status updates from healthy to unhealthy after initial reporting delay should happen with no delay // // Initial stream status of healthy and monitor activation time to t=0 - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, 1, gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::stream_status, 1, gate); long long current_time = 5; int expected_status = 3; @@ -972,19 +972,19 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) gate); BST_REQUIRE(success); - const auto actual_value = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); + const auto actual_value = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); // Expected status to be unhealthy BST_CHECK_EQUAL(expected_status, actual_value.as_integer()); // Expected status pending received time to be 0 i.e. not pending - const auto actual_time = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); + const auto actual_time = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); BST_CHECK_EQUAL(0, actual_time.as_integer()); } { // Status updates from partailly unhealthy to unhealthy after initial reporting delay should happen with no delay // // Initial stream status of partially unhealthy and monitor activation time to t=0 - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, 2, gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::stream_status, 2, gate); long long current_time = 5; int expected_status = 3; @@ -1002,11 +1002,11 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) gate); BST_REQUIRE(success); - const auto actual_value = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); + const auto actual_value = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); // Expected status to go to unhealthy without delay BST_CHECK_EQUAL(expected_status, actual_value.as_integer()); // Expected status pending received time to be 0 i.e. not pending - const auto actual_time = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); + const auto actual_time = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); BST_CHECK_EQUAL(0, actual_time.as_integer()); } { @@ -1014,8 +1014,8 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) // // Initial stream status of partially unhealthy and monitor activation time to t=0 int initial_status = 2; - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, initial_status, gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::stream_status, initial_status, gate); long long current_time = 5; int expected_status = 1; @@ -1033,14 +1033,14 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) gate); BST_REQUIRE(success); - const auto actual_value = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); + const auto actual_value = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); // Expected status to stay partially unhealthy BST_CHECK_EQUAL(initial_status, actual_value.as_integer()); // Expected status pending received time to be current time i.e. pending - const auto actual_time = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); + const auto actual_time = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); BST_CHECK_EQUAL(current_time, actual_time.as_integer()); // Expected status pending to be expected status i.e. healthy - const auto actual_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending, gate); + const auto actual_status = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending, gate); BST_CHECK_EQUAL(expected_status, actual_status.as_integer()); } { @@ -1048,15 +1048,15 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) // // Initial stream status of partially unhealthy and monitor activation time to t=0 int initial_status = 2; - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, initial_status, gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::stream_status, initial_status, gate); long long current_time = 9; int expected_status = 1; long long received_time = 8; // Status already pending with healthy state at t=8 - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending, expected_status, gate); - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, received_time, gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending, expected_status, gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, received_time, gate); // Set stream stream status to healthy at t=9 - healthy status already pending bool success = nmos::nc::details::set_monitor_status_with_delay(resources, monitor_oid, expected_status, U(""), @@ -1072,14 +1072,14 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) gate); BST_REQUIRE(success); - const auto actual_value = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); + const auto actual_value = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); // Expected status to stay unhealthy BST_CHECK_EQUAL(initial_status, actual_value.as_integer()); // Expected status pending received time to be the original pending time - const auto actual_time = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); + const auto actual_time = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending_received_time, gate); BST_CHECK_EQUAL(received_time, actual_time.as_integer()); // Expected status pending to be expected status i.e. healthy - const auto actual_status = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending, gate); + const auto actual_status = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status_pending, gate); BST_CHECK_EQUAL(expected_status, actual_status.as_integer()); } { @@ -1088,9 +1088,9 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) // Initial stream status of healthy and monitor activation time to t=0 int initial_status = 1; utility::string_t initial_status_message = U("initial status message"); - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, initial_status, gate); - nmos::nc::set_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_message, web::json::value::string(initial_status_message), gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::monitor_activation_time, 0, gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::stream_status, initial_status, gate); + nmos::nc::set_property(resources, monitor_oid, nmos::fields::nc::stream_status_message, web::json::value::string(initial_status_message), gate); long long current_time = 9; utility::string_t updated_status_message = U("updated status message"); @@ -1109,10 +1109,10 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) gate); BST_REQUIRE(success); - const auto actual_value = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); + const auto actual_value = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status, gate); // Expected status to stay healthy BST_CHECK_EQUAL(initial_status, actual_value.as_integer()); - const auto actual_status_message = nmos::nc::get_control_protocol_property(resources, monitor_oid, nmos::fields::nc::stream_status_message, gate); + const auto actual_status_message = nmos::nc::get_property(resources, monitor_oid, nmos::fields::nc::stream_status_message, gate); // Expected message to have been updated BST_CHECK_EQUAL(updated_status_message, actual_status_message.as_string()); } From 5ac846dc2c5f41d7a45e8e359559fc8855d27fa7 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 12 Sep 2025 11:41:47 +0100 Subject: [PATCH 242/250] Move control_protocol_methods into the nc namespace --- Development/nmos/configuration_api.cpp | 4 +- Development/nmos/control_protocol_methods.cpp | 1159 +++++++++-------- Development/nmos/control_protocol_methods.h | 72 +- Development/nmos/control_protocol_state.cpp | 32 +- .../test/control_protocol_methods_test.cpp | 4 +- 5 files changed, 638 insertions(+), 633 deletions(-) diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 8e4d3034f..c0280d207 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -477,7 +477,7 @@ namespace nmos { nmos::fields::nc::id, details::make_nc_property_id(details::parse_formatted_property_id(property_id))}, }); - auto result = get(*resource, arguments, false, get_control_protocol_class_descriptor, gate_); + auto result = nc::get(*resource, arguments, false, get_control_protocol_class_descriptor, gate_); auto status = nmos::fields::nc::status(result); auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; set_reply(res, code, result); @@ -605,7 +605,7 @@ namespace nmos { nmos::fields::nc::value, nmos::fields::nc::value(body)} }); - auto result = set(resources, *resource, arguments, false, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate); + auto result = nc::set(resources, *resource, arguments, false, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate); auto status = nmos::fields::nc::status(result); auto code = (nc_method_status::ok == status || nc_method_status::property_deprecated == status) ? status_codes::OK : status_codes::InternalError; diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index e6e4bf26f..088d290fa 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -10,783 +10,786 @@ namespace nmos { - // NcObject methods implementation - // Get property value - web::json::value get(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + namespace nc { - // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - - const auto& property_id = nmos::fields::nc::id(arguments); - - slog::log(gate, SLOG_FLF) << "Get property: " << property_id.serialize(); - - // find the relevant nc_property_descriptor - const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); - if (!property.is_null()) + // NcObject methods implementation + // Get property value + web::json::value get(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { - return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, resource.data.at(nmos::fields::nc::name(property))); - } + // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - // unknown property - utility::ostringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do Get"); - slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::property_not_implemented }, ss.str()); - } + const auto& property_id = nmos::fields::nc::id(arguments); - // Set property value - web::json::value set(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) - { - // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... + slog::log(gate, SLOG_FLF) << "Get property: " << property_id.serialize(); - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& val = nmos::fields::nc::value(arguments); + // find the relevant nc_property_descriptor + const auto& property = nc::find_property_descriptor(nmos::details::parse_nc_property_id(property_id), nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + if (!property.is_null()) + { + return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, resource.data.at(nmos::fields::nc::name(property))); + } - slog::log(gate, SLOG_FLF) << "Set property: " << property_id.serialize() << " value: " << val.serialize(); + // unknown property + utility::ostringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do Get"); + slog::log(gate, SLOG_FLF) << ss.str(); + return nmos::details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); + } - // find the relevant nc_property_descriptor - const auto property_id_ = details::parse_nc_property_id(property_id); - const auto& property = nc::find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); - if (!property.is_null()) + // Set property value + web::json::value set(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { - if (nmos::fields::nc::is_read_only(property)) - { - utility::ostringstream_t ss; - ss << U("can not set read only property: ") << property_id.serialize(); - slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::read_only }, ss.str()); - } + // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - if ((val.is_null() && !nmos::fields::nc::is_nullable(property)) - || (!val.is_array() && nmos::fields::nc::is_sequence(property)) - || (val.is_array() && !nmos::fields::nc::is_sequence(property))) - { - utility::ostringstream_t ss; - ss << U("parameter error: cannot set value: ") << val.serialize() << U(" on property: ") << property_id.serialize(); - slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::parameter_error }, ss.str()); - } + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& val = nmos::fields::nc::value(arguments); - // Special case for BCP-008-01/02 where it specifies that status monitors cannot be disabled - if (nmos::fields::nc::name(property).c_str() == nmos::fields::nc::enabled.key - && nc::is_status_monitor(details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data))) - && !val.as_bool()) - { - utility::ostringstream_t ss; - ss << U("invalid request: cannot disable NcStatusMonitors"); - slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::invalid_request }, ss.str()); - } + slog::log(gate, SLOG_FLF) << "Set property: " << property_id.serialize() << " value: " << val.serialize(); - try + // find the relevant nc_property_descriptor + const auto property_id_ = nmos::details::parse_nc_property_id(property_id); + const auto& property = nc::find_property_descriptor(property_id_, nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + if (!property.is_null()) { - // do property constraints validation - nmos::nc::details::constraints_validation(val, nc::details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { nc::details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); + if (nmos::fields::nc::is_read_only(property)) + { + utility::ostringstream_t ss; + ss << U("can not set read only property: ") << property_id.serialize(); + slog::log(gate, SLOG_FLF) << ss.str(); + return nmos::details::make_nc_method_result_error({nc_method_status::read_only}, ss.str()); + } - // update property - nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) + if ((val.is_null() && !nmos::fields::nc::is_nullable(property)) + || (!val.is_array() && nmos::fields::nc::is_sequence(property)) + || (val.is_array() && !nmos::fields::nc::is_sequence(property))) { - resource.data[nmos::fields::nc::name(property)] = val; + utility::ostringstream_t ss; + ss << U("parameter error: cannot set value: ") << val.serialize() << U(" on property: ") << property_id.serialize(); + slog::log(gate, SLOG_FLF) << ss.str(); + return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + } - // do notification that the specified property has changed - if (property_changed) + // Special case for BCP-008-01/02 where it specifies that status monitors cannot be disabled + if (nmos::fields::nc::name(property).c_str() == nmos::fields::nc::enabled.key + && nc::is_status_monitor(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data))) + && !val.as_bool()) + { + utility::ostringstream_t ss; + ss << U("invalid request: cannot disable NcStatusMonitors"); + slog::log(gate, SLOG_FLF) << ss.str(); + return nmos::details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + } + + try + { + // do property constraints validation + nmos::nc::details::constraints_validation(val, nc::details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), {nc::details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor}); + + // update property + nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) { - property_changed(resource, nmos::fields::nc::name(property), -1); - } + resource.data[nmos::fields::nc::name(property)] = val; - }, make_property_changed_event(nmos::fields::nc::oid(resource.data), { { property_id_, nc_property_change_type::type::value_changed, val } })); + // do notification that the specified property has changed + if (property_changed) + { + property_changed(resource, nmos::fields::nc::name(property), -1); + } - return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); + }, make_property_changed_event(nmos::fields::nc::oid(resource.data), {{property_id_, nc_property_change_type::type::value_changed, val}})); + + return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}); + } + catch (const nmos::control_protocol_exception& e) + { + utility::ostringstream_t ss; + ss << "Set property: " << property_id.serialize() << " value: " << val.serialize() << " error: " << e.what(); + slog::log(gate, SLOG_FLF) << ss.str(); + return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + } } - catch (const nmos::control_protocol_exception& e) + + // unknown property + utility::ostringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << " to do Set"; + slog::log(gate, SLOG_FLF) << ss.str(); + return nmos::details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); + } + + // Get sequence item + web::json::value get_sequence_item(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + { + // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... + + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& index = nmos::fields::nc::index(arguments); + + slog::log(gate, SLOG_FLF) << "Get sequence item: " << property_id.serialize() << " index: " << index; + + // find the relevant nc_property_descriptor + const auto& property = nc::find_property_descriptor(nmos::details::parse_nc_property_id(property_id), nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + if (!property.is_null()) { + const auto& data = resource.data.at(nmos::fields::nc::name(property)); + + if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) + { + // property is not a sequence + utility::ostringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceItem"); + return nmos::details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + } + + if (data.as_array().size() > (size_t)index) + { + return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, data.at(index)); + } + + // out of bound utility::ostringstream_t ss; - ss << "Set property: " << property_id.serialize() << " value: " << val.serialize() << " error: " << e.what(); + ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do GetSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::parameter_error }, ss.str()); + return nmos::details::make_nc_method_result_error({nc_method_status::index_out_of_bounds}, ss.str()); } + + // unknown property + utility::ostringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << U(" to do GetSequenceItem"); + slog::log(gate, SLOG_FLF) << ss.str(); + return nmos::details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } - // unknown property - utility::ostringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << " to do Set"; - slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::property_not_implemented }, ss.str()); - } + // Set sequence item + web::json::value set_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + { + // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - // Get sequence item - web::json::value get_sequence_item(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& index = nmos::fields::nc::index(arguments); + const auto& val = nmos::fields::nc::value(arguments); - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& index = nmos::fields::nc::index(arguments); + slog::log(gate, SLOG_FLF) << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize(); - slog::log(gate, SLOG_FLF) << "Get sequence item: " << property_id.serialize() << " index: " << index; + // find the relevant nc_property_descriptor + const auto property_id_ = nmos::details::parse_nc_property_id(property_id); + const auto& property = nc::find_property_descriptor(property_id_, nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + if (!property.is_null()) + { + if (nmos::fields::nc::is_read_only(property)) + { + return nmos::details::make_nc_method_result({nc_method_status::read_only}); + } - // find the relevant nc_property_descriptor - const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); - if (!property.is_null()) - { - const auto& data = resource.data.at(nmos::fields::nc::name(property)); + auto& data = resource.data.at(nmos::fields::nc::name(property)); - if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) - { - // property is not a sequence - utility::ostringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceItem"); - return details::make_nc_method_result_error({ nc_method_status::invalid_request }, ss.str()); - } + if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) + { + // property is not a sequence + utility::ostringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do SetSequenceItem"); + slog::log(gate, SLOG_FLF) << ss.str(); + return nmos::details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + } - if (data.as_array().size() > (size_t)index) - { - return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, data.at(index)); + if (data.as_array().size() > (size_t)index) + { + try + { + // do property constraints validation + nmos::nc::details::constraints_validation(val, nc::details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), {nc::details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor}); + + // update property + nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) + { + resource.data[nmos::fields::nc::name(property)][index] = val; + + // do notification that the specified property has changed + if (property_changed) + { + property_changed(resource, nmos::fields::nc::name(property), index); + } + + }, make_property_changed_event(nmos::fields::nc::oid(resource.data), {{property_id_, nc_property_change_type::type::sequence_item_changed, val, nc_id(index)}})); + + return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}); + } + catch (const nmos::control_protocol_exception& e) + { + utility::ostringstream_t ss; + ss << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize() << " error: " << e.what(); + slog::log(gate, SLOG_FLF) << ss.str(); + return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + } + } + + // out of bound + utility::ostringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do SetSequenceItem"); + slog::log(gate, SLOG_FLF) << ss.str(); + return nmos::details::make_nc_method_result_error({nc_method_status::index_out_of_bounds}, ss.str()); } - // out of bound + // unknown property utility::ostringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do GetSequenceItem"); + ss << U("unknown property: ") << property_id.serialize() << U(" to do SetSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::index_out_of_bounds }, ss.str()); + return nmos::details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } - // unknown property - utility::ostringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do GetSequenceItem"); - slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::property_not_implemented }, ss.str()); - } + // Add item to sequence + web::json::value add_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) + { + // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - // Set sequence item - web::json::value set_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) - { - // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... + using web::json::value; - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& index = nmos::fields::nc::index(arguments); - const auto& val = nmos::fields::nc::value(arguments); + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& val = nmos::fields::nc::value(arguments); - slog::log(gate, SLOG_FLF) << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize(); + slog::log(gate, SLOG_FLF) << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize(); - // find the relevant nc_property_descriptor - const auto property_id_ = details::parse_nc_property_id(property_id); - const auto& property = nc::find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); - if (!property.is_null()) - { - if (nmos::fields::nc::is_read_only(property)) + // find the relevant nc_property_descriptor + const auto property_id_ = nmos::details::parse_nc_property_id(property_id); + const auto& property = nc::find_property_descriptor(property_id_, nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + if (!property.is_null()) { - return details::make_nc_method_result({ nc_method_status::read_only }); - } + if (nmos::fields::nc::is_read_only(property)) + { + return nmos::details::make_nc_method_result({nc_method_status::read_only}); + } - auto& data = resource.data.at(nmos::fields::nc::name(property)); + if (!nmos::fields::nc::is_sequence(property)) + { + // property is not a sequence + utility::ostringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do AddSequenceItem"); + return nmos::details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + } - if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) - { - // property is not a sequence - utility::ostringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do SetSequenceItem"); - slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::invalid_request }, ss.str()); - } + auto& data = resource.data.at(nmos::fields::nc::name(property)); + + const nc_id sequence_item_index = data.is_null() ? 0 : nc_id(data.as_array().size()); - if (data.as_array().size() > (size_t)index) - { try { // do property constraints validation - nmos::nc::details::constraints_validation(val, nc::details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { nc::details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); + nmos::nc::details::constraints_validation(val, nc::details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), {nc::details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor}); // update property nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) { - resource.data[nmos::fields::nc::name(property)][index] = val; + auto& sequence = resource.data[nmos::fields::nc::name(property)]; + if (data.is_null()) { sequence = value::array(); } + web::json::push_back(sequence, val); // do notification that the specified property has changed if (property_changed) { - property_changed(resource, nmos::fields::nc::name(property), index); + property_changed(resource, nmos::fields::nc::name(property), (int)sequence.as_array().size() - 1); } - }, make_property_changed_event(nmos::fields::nc::oid(resource.data), { { property_id_, nc_property_change_type::type::sequence_item_changed, val, nc_id(index) } })); + }, make_property_changed_event(nmos::fields::nc::oid(resource.data), {{property_id_, nc_property_change_type::type::sequence_item_added, val, sequence_item_index}})); - return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); + return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, sequence_item_index); } catch (const nmos::control_protocol_exception& e) { utility::ostringstream_t ss; - ss << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize() << " error: " << e.what(); + ss << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize() << " error: " << e.what(); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::parameter_error }, ss.str()); + return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); } } - // out of bound + // unknown property utility::ostringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do SetSequenceItem"); + ss << U("unknown property: ") << property_id.serialize() << U(" to do AddSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::index_out_of_bounds }, ss.str()); + return nmos::details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } - // unknown property - utility::ostringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do SetSequenceItem"); - slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::property_not_implemented }, ss.str()); - } - - // Add item to sequence - web::json::value add_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) - { - // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - - using web::json::value; - - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& val = nmos::fields::nc::value(arguments); - - slog::log(gate, SLOG_FLF) << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize(); - - // find the relevant nc_property_descriptor - const auto property_id_ = details::parse_nc_property_id(property_id); - const auto& property = nc::find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); - if (!property.is_null()) + // Delete sequence item + web::json::value remove_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) { - if (nmos::fields::nc::is_read_only(property)) - { - return details::make_nc_method_result({ nc_method_status::read_only }); - } + // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - if (!nmos::fields::nc::is_sequence(property)) - { - // property is not a sequence - utility::ostringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do AddSequenceItem"); - return details::make_nc_method_result_error({ nc_method_status::invalid_request }, ss.str()); - } - - auto& data = resource.data.at(nmos::fields::nc::name(property)); + const auto& property_id = nmos::fields::nc::id(arguments); + const auto& index = nmos::fields::nc::index(arguments); - const nc_id sequence_item_index = data.is_null() ? 0 : nc_id(data.as_array().size()); + slog::log(gate, SLOG_FLF) << "Remove sequence item: " << property_id.serialize() << " index: " << index; - try + // find the relevant nc_property_descriptor + const auto& property = nc::find_property_descriptor(nmos::details::parse_nc_property_id(property_id), nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + if (!property.is_null()) { - // do property constraints validation - nmos::nc::details::constraints_validation(val, nc::details::get_runtime_property_constraints(property_id_, resource.data.at(nmos::fields::nc::runtime_property_constraints)), nmos::fields::nc::constraints(property), { nc::details::get_datatype_descriptor(property.at(nmos::fields::nc::type_name), get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor }); - - // update property - nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) + if (nmos::fields::nc::is_read_only(property)) { - auto& sequence = resource.data[nmos::fields::nc::name(property)]; - if (data.is_null()) { sequence = value::array(); } - web::json::push_back(sequence, val); - - // do notification that the specified property has changed - if (property_changed) - { - property_changed(resource, nmos::fields::nc::name(property), (int)sequence.as_array().size()-1); - } - - }, make_property_changed_event(nmos::fields::nc::oid(resource.data), { { property_id_, nc_property_change_type::type::sequence_item_added, val, sequence_item_index } })); - - return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, sequence_item_index); - } - catch (const nmos::control_protocol_exception& e) - { - utility::ostringstream_t ss; - ss << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize() << " error: " << e.what(); - slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::parameter_error }, ss.str()); - } - } + return nmos::details::make_nc_method_result({nc_method_status::read_only}); + } - // unknown property - utility::ostringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do AddSequenceItem"); - slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::property_not_implemented }, ss.str()); - } + const auto& data = resource.data.at(nmos::fields::nc::name(property)); - // Delete sequence item - web::json::value remove_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate) - { - // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... + if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) + { + // property is not a sequence + utility::ostringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do RemoveSequenceItem"); + slog::log(gate, SLOG_FLF) << ss.str(); + return nmos::details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + } - const auto& property_id = nmos::fields::nc::id(arguments); - const auto& index = nmos::fields::nc::index(arguments); + if (data.as_array().size() > (size_t)index) + { + nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) + { + auto& sequence = resource.data[nmos::fields::nc::name(property)].as_array(); + sequence.erase(index); - slog::log(gate, SLOG_FLF) << "Remove sequence item: " << property_id.serialize() << " index: " << index; + // do notification that the specified property has changed + if (property_changed) + { + property_changed(resource, nmos::fields::nc::name(property), -2); + } - // find the relevant nc_property_descriptor - const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); - if (!property.is_null()) - { - if (nmos::fields::nc::is_read_only(property)) - { - return details::make_nc_method_result({ nc_method_status::read_only }); - } + }, make_property_changed_event(nmos::fields::nc::oid(resource.data), {{nmos::details::parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_removed, nc_id(index)}})); - const auto& data = resource.data.at(nmos::fields::nc::name(property)); + return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}); + } - if (!nmos::fields::nc::is_sequence(property) || data.is_null() || !data.is_array()) - { - // property is not a sequence + // out of bound utility::ostringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do RemoveSequenceItem"); + ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do RemoveSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::invalid_request }, ss.str()); - } - - if (data.as_array().size() > (size_t)index) - { - nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) - { - auto& sequence = resource.data[nmos::fields::nc::name(property)].as_array(); - sequence.erase(index); - - // do notification that the specified property has changed - if (property_changed) - { - property_changed(resource, nmos::fields::nc::name(property), -2); - } - - }, make_property_changed_event(nmos::fields::nc::oid(resource.data), { { details::parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_removed, nc_id(index) } })); - - return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }); + return nmos::details::make_nc_method_result_error({nc_method_status::index_out_of_bounds}, ss.str()); } - // out of bound + // unknown property utility::ostringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do RemoveSequenceItem"); + ss << U("unknown property: ") << property_id.serialize() << U(" to do RemoveSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::index_out_of_bounds }, ss.str()); + return nmos::details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } - // unknown property - utility::ostringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << U(" to do RemoveSequenceItem"); - slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::property_not_implemented }, ss.str()); - } - - // Get sequence length - web::json::value get_sequence_length(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... + // Get sequence length + web::json::value get_sequence_length(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) + { + // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - using web::json::value; + using web::json::value; - const auto& property_id = nmos::fields::nc::id(arguments); + const auto& property_id = nmos::fields::nc::id(arguments); - slog::log(gate, SLOG_FLF) << "Get sequence length: " << property_id.serialize(); + slog::log(gate, SLOG_FLF) << "Get sequence length: " << property_id.serialize(); - // find the relevant nc_property_descriptor - const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); - if (!property.is_null()) - { - if (!nmos::fields::nc::is_sequence(property)) + // find the relevant nc_property_descriptor + const auto& property = nc::find_property_descriptor(nmos::details::parse_nc_property_id(property_id), nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + if (!property.is_null()) { - // property is not a sequence - utility::ostringstream_t ss; - ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceLength"); - slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::invalid_request }, ss.str()); - } + if (!nmos::fields::nc::is_sequence(property)) + { + // property is not a sequence + utility::ostringstream_t ss; + ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceLength"); + slog::log(gate, SLOG_FLF) << ss.str(); + return nmos::details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + } - const auto& data = resource.data.at(nmos::fields::nc::name(property)); + const auto& data = resource.data.at(nmos::fields::nc::name(property)); - if (nmos::fields::nc::is_nullable(property)) - { - // can be null - if (data.is_null()) + if (nmos::fields::nc::is_nullable(property)) { - // null - return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, value::null()); + // can be null + if (data.is_null()) + { + // null + return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, value::null()); + } } - } - else - { - // cannot be null - if (data.is_null()) + else { - // null - utility::ostringstream_t ss; - ss << U("property: ") << property_id.serialize() << " is a null sequence to do GetSequenceLength"; - slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::invalid_request }, ss.str()); + // cannot be null + if (data.is_null()) + { + // null + utility::ostringstream_t ss; + ss << U("property: ") << property_id.serialize() << " is a null sequence to do GetSequenceLength"; + slog::log(gate, SLOG_FLF) << ss.str(); + return nmos::details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + } } + return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, value(uint32_t(data.as_array().size()))); } - return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok }, value(uint32_t(data.as_array().size()))); - } - - // unknown property - utility::ostringstream_t ss; - ss << U("unknown property: ") << property_id.serialize() << " to do GetSequenceLength"; - slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::property_not_implemented }, ss.str()); - } - // NcBlock methods implementation - // Gets descriptors of members of the block - web::json::value get_member_descriptors(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) - { - // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... + // unknown property + utility::ostringstream_t ss; + ss << U("unknown property: ") << property_id.serialize() << " to do GetSequenceLength"; + slog::log(gate, SLOG_FLF) << ss.str(); + return nmos::details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); + } - using web::json::value; + // NcBlock methods implementation + // Gets descriptors of members of the block + web::json::value get_member_descriptors(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + { + // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - const auto& recurse = nmos::fields::nc::recurse(arguments); // If recurse is set to true, nested members is to be retrieved + using web::json::value; - slog::log(gate, SLOG_FLF) << "Get descriptors of members of the block: " << "recurse: " << recurse; + const auto& recurse = nmos::fields::nc::recurse(arguments); // If recurse is set to true, nested members is to be retrieved - auto descriptors = value::array(); - nmos::nc::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); + slog::log(gate, SLOG_FLF) << "Get descriptors of members of the block: " << "recurse: " << recurse; - return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); - } + auto descriptors = value::array(); + nmos::nc::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); - // Finds member(s) by path - web::json::value find_members_by_path(nmos::resources& resources, const nmos::resource& resource_, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) - { - // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... + return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); + } - using web::json::value; + // Finds member(s) by path + web::json::value find_members_by_path(nmos::resources& resources, const nmos::resource& resource_, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) + { + // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - // Relative path to search for (MUST not include the role of the block targeted by oid) - const auto& path = arguments.at(nmos::fields::nc::path); + using web::json::value; - slog::log(gate, SLOG_FLF) << "Find member(s) by path: " << "path: " << path.serialize(); + // Relative path to search for (MUST not include the role of the block targeted by oid) + const auto& path = arguments.at(nmos::fields::nc::path); - if (0 == path.size()) - { - // empty path - return details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("empty path to do FindMembersByPath")); - } + slog::log(gate, SLOG_FLF) << "Find member(s) by path: " << "path: " << path.serialize(); - auto descriptors = value::array(); - value descriptor; + if (0 == path.size()) + { + // empty path + return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty path to do FindMembersByPath")); + } - nmos::resource resource = resource_; - for (const auto& role : path.as_array()) - { - // look for the role in members + auto descriptors = value::array(); + value descriptor; - if (resource.data.has_field(nmos::fields::nc::members)) + nmos::resource resource = resource_; + for (const auto& role : path.as_array()) { - auto& members = nmos::fields::nc::members(resource.data); - auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& nc_block_member_descriptor) - { - return role.as_string() == nmos::fields::nc::role(nc_block_member_descriptor); - }); + // look for the role in members - if (members.end() != member_found) + if (resource.data.has_field(nmos::fields::nc::members)) { - descriptor = *member_found; + auto& members = nmos::fields::nc::members(resource.data); + auto member_found = std::find_if(members.begin(), members.end(), [&](const web::json::value& nc_block_member_descriptor) + { + return role.as_string() == nmos::fields::nc::role(nc_block_member_descriptor); + }); + + if (members.end() != member_found) + { + descriptor = *member_found; - // use oid to look for the next resource - resource = *nmos::find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(*member_found)))); + // use oid to look for the next resource + resource = *nmos::find_resource(resources, utility::s2us(std::to_string(nmos::fields::nc::oid(*member_found)))); + } + else + { + // no role + utility::ostringstream_t ss; + ss << U("role: ") << role.as_string() << U(" not found to do FindMembersByPath"); + slog::log(gate, SLOG_FLF) << ss.str(); + return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + } } else { - // no role + // no members utility::ostringstream_t ss; - ss << U("role: ") << role.as_string() << U(" not found to do FindMembersByPath"); + ss << U("role: ") << role.as_string() << U(" has no members to do FindMembersByPath"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::parameter_error }, ss.str()); + return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); } } - else - { - // no members - utility::ostringstream_t ss; - ss << U("role: ") << role.as_string() << U(" has no members to do FindMembersByPath"); - slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::parameter_error }, ss.str()); - } - } - - web::json::push_back(descriptors, descriptor); - return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); - } - // Finds members with given role name or fragment - web::json::value find_members_by_role(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) - { - // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - - using web::json::value; - - const auto& role = nmos::fields::nc::role(arguments); // Role text to search for - const auto& case_sensitive = nmos::fields::nc::case_sensitive(arguments); // Signals if the comparison should be case sensitive - const auto& match_whole_string = nmos::fields::nc::match_whole_string(arguments); // TRUE to only return exact matches - const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks - - slog::log(gate, SLOG_FLF) << "Find members with given role name or fragment: " << "role: " << role; + web::json::push_back(descriptors, descriptor); + return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); + } - if (role.empty()) + // Finds members with given role name or fragment + web::json::value find_members_by_role(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - // empty role - return details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("empty role to do FindMembersByRole")); - } + // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - auto descriptors = value::array(); - nmos::nc::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); + using web::json::value; - return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); - } + const auto& role = nmos::fields::nc::role(arguments); // Role text to search for + const auto& case_sensitive = nmos::fields::nc::case_sensitive(arguments); // Signals if the comparison should be case sensitive + const auto& match_whole_string = nmos::fields::nc::match_whole_string(arguments); // TRUE to only return exact matches + const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks - // Finds members with given class id - web::json::value find_members_by_class_id(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) - { - // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... + slog::log(gate, SLOG_FLF) << "Find members with given role name or fragment: " << "role: " << role; - using web::json::value; + if (role.empty()) + { + // empty role + return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty role to do FindMembersByRole")); + } - const auto class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for - const auto& include_derived = nmos::fields::nc::include_derived(arguments); // If TRUE it will also include derived class descriptors - const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks + auto descriptors = value::array(); + nmos::nc::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); - slog::log(gate, SLOG_FLF) << "Find members with given class id: " << "class_id: " << nmos::details::make_nc_class_id(class_id).serialize(); + return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); + } - if (class_id.empty()) + // Finds members with given class id + web::json::value find_members_by_class_id(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - // empty class_id - return details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("empty classId to do FindMembersByClassId")); - } + // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... + using web::json::value; - auto descriptors = value::array(); - nmos::nc::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); + const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for + const auto& include_derived = nmos::fields::nc::include_derived(arguments); // If TRUE it will also include derived class descriptors + const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks - return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptors); - } + slog::log(gate, SLOG_FLF) << "Find members with given class id: " << "class_id: " << nmos::details::make_nc_class_id(class_id).serialize(); - // NcClassManager methods implementation - // Get a single class descriptor - web::json::value get_control_class(const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) - { - using web::json::value; + if (class_id.empty()) + { + // empty class_id + return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty classId to do FindMembersByClassId")); + } - const auto class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for - const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements + // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - slog::log(gate, SLOG_FLF) << "Get a single class descriptor: " << "class_id: " << nmos::details::make_nc_class_id(class_id).serialize(); + auto descriptors = value::array(); + nmos::nc::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); - if (class_id.empty()) - { - // empty class_id - return details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("empty classId to do GetControlClass")); + return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); } - // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - - const auto& control_class = get_control_protocol_class_descriptor(class_id); - if (!control_class.class_id.empty()) + // NcClassManager methods implementation + // Get a single class descriptor + web::json::value get_control_class(const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { - auto& description = control_class.description; - auto& name = control_class.name; - auto& fixed_role = control_class.fixed_role; - auto property_descriptors = control_class.property_descriptors; - auto method_descriptors = value::array(); - for (const auto& method_descriptor : control_class.method_descriptors) { web::json::push_back(method_descriptors, std::get<0>(method_descriptor)); } - auto event_descriptors = control_class.event_descriptors; - - if (include_inherited) + using web::json::value; + + const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for + const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements + + slog::log(gate, SLOG_FLF) << "Get a single class descriptor: " << "class_id: " << nmos::details::make_nc_class_id(class_id).serialize(); + + if (class_id.empty()) { - auto inherited_class_id = class_id; - inherited_class_id.pop_back(); + // empty class_id + return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty classId to do GetControlClass")); + } + + // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - while (!inherited_class_id.empty()) + const auto& control_class = get_control_protocol_class_descriptor(class_id); + if (!control_class.class_id.empty()) + { + auto& description = control_class.description; + auto& name = control_class.name; + auto& fixed_role = control_class.fixed_role; + auto property_descriptors = control_class.property_descriptors; + auto method_descriptors = value::array(); + for (const auto& method_descriptor : control_class.method_descriptors) { web::json::push_back(method_descriptors, std::get<0>(method_descriptor)); } + auto event_descriptors = control_class.event_descriptors; + + if (include_inherited) { - const auto& inherited_control_class = get_control_protocol_class_descriptor(inherited_class_id); + auto inherited_class_id = class_id; + inherited_class_id.pop_back(); + + while (!inherited_class_id.empty()) { - for (const auto& property_descriptor : inherited_control_class.property_descriptors.as_array()) { web::json::push_back(property_descriptors, property_descriptor); } - for (const auto& method_descriptor : inherited_control_class.method_descriptors) { web::json::push_back(method_descriptors, std::get<0>(method_descriptor)); } - for (const auto& event_descriptor : inherited_control_class.event_descriptors.as_array()) { web::json::push_back(event_descriptors, event_descriptor); } + const auto& inherited_control_class = get_control_protocol_class_descriptor(inherited_class_id); + { + for (const auto& property_descriptor : inherited_control_class.property_descriptors.as_array()) { web::json::push_back(property_descriptors, property_descriptor); } + for (const auto& method_descriptor : inherited_control_class.method_descriptors) { web::json::push_back(method_descriptors, std::get<0>(method_descriptor)); } + for (const auto& event_descriptor : inherited_control_class.event_descriptors.as_array()) { web::json::push_back(event_descriptors, event_descriptor); } + } + inherited_class_id.pop_back(); } - inherited_class_id.pop_back(); } + const auto descriptor = fixed_role.is_null() + ? nmos::details::make_nc_class_descriptor(description, class_id, name, property_descriptors, method_descriptors, event_descriptors) + : nmos::details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), property_descriptors, method_descriptors, event_descriptors); + + return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptor); } - const auto descriptor = fixed_role.is_null() - ? details::make_nc_class_descriptor(description, class_id, name, property_descriptors, method_descriptors, event_descriptors) - : details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), property_descriptors, method_descriptors, event_descriptors); - return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptor); + return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, U("classId not found")); } - return details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("classId not found")); - } - - // Get a single datatype descriptor - web::json::value get_datatype(const web::json::value& arguments, bool is_deprecated, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, slog::base_gate& gate) - { - // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - - const auto& name = nmos::fields::nc::name(arguments); // name of datatype - const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements + // Get a single datatype descriptor + web::json::value get_datatype(const web::json::value& arguments, bool is_deprecated, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, slog::base_gate& gate) + { + // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... - slog::log(gate, SLOG_FLF) << "Get a single datatype descriptor: " << "name: " << name; + const auto& name = nmos::fields::nc::name(arguments); // name of datatype + const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements - if (name.empty()) - { - // empty name - return details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("empty name to do GetDatatype")); - } + slog::log(gate, SLOG_FLF) << "Get a single datatype descriptor: " << "name: " << name; - const auto& datatype = get_control_protocol_datatype_descriptor(name); - if (datatype.descriptor.size()) - { - auto descriptor = datatype.descriptor; + if (name.empty()) + { + // empty name + return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty name to do GetDatatype")); + } - if (include_inherited) + const auto& datatype = get_control_protocol_datatype_descriptor(name); + if (datatype.descriptor.size()) { - const auto& type = nmos::fields::nc::type(descriptor); - if (nc_datatype_type::Struct == type) - { - auto descriptor_ = descriptor; + auto descriptor = datatype.descriptor; - for (;;) + if (include_inherited) + { + const auto& type = nmos::fields::nc::type(descriptor); + if (nc_datatype_type::Struct == type) { - const auto& parent_type = descriptor_.at(nmos::fields::nc::parent_type); - if (!parent_type.is_null()) + auto descriptor_ = descriptor; + + for (;;) { - const auto& parent_datatype = get_control_protocol_datatype_descriptor(parent_type.as_string()); - if (parent_datatype.descriptor.size()) + const auto& parent_type = descriptor_.at(nmos::fields::nc::parent_type); + if (!parent_type.is_null()) { - descriptor_ = parent_datatype.descriptor; - - const auto& fields = nmos::fields::nc::fields(descriptor_); - for (const auto& field : fields) + const auto& parent_datatype = get_control_protocol_datatype_descriptor(parent_type.as_string()); + if (parent_datatype.descriptor.size()) { - web::json::push_back(descriptor.at(nmos::fields::nc::fields), field); + descriptor_ = parent_datatype.descriptor; + + const auto& fields = nmos::fields::nc::fields(descriptor_); + for (const auto& field : fields) + { + web::json::push_back(descriptor.at(nmos::fields::nc::fields), field); + } } } - } - else - { - break; + else + { + break; + } } } } + + return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptor); } - return details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, descriptor); + return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, U("name not found")); } - return details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("name not found")); - } - - // NcReceiverMonitor methods implementation - namespace details - { - web::json::value get_packet_counters(bool is_deprecated, get_packet_counters_handler get_packet_counters) + // NcReceiverMonitor methods implementation + namespace details { - using web::json::value; - using web::json::value_from_elements; - - if (get_packet_counters) + web::json::value get_packet_counters(bool is_deprecated, get_packet_counters_handler get_packet_counters) { - const auto counters = get_packet_counters(); - auto nc_counter_sequence = value_from_elements(counters | boost::adaptors::transformed([](const nc::counter& counter) + using web::json::value; + using web::json::value_from_elements; + + if (get_packet_counters) { - return web::json::value_of({ - { nmos::fields::nc::name, value::string(counter.name) }, - { nmos::fields::nc::value, value::number(counter.value) }, - { nmos::fields::nc::description, value::string(counter.description) } - }); - })); + const auto counters = get_packet_counters(); + auto nc_counter_sequence = value_from_elements(counters | boost::adaptors::transformed([](const nc::counter& counter) + { + return web::json::value_of({ + {nmos::fields::nc::name, value::string(counter.name)}, + {nmos::fields::nc::value, value::number(counter.value)}, + {nmos::fields::nc::description, value::string(counter.description)} + }); + })); + + return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, nc_counter_sequence); + } - return nmos::details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }, nc_counter_sequence); + return nmos::details::make_nc_method_result_error({nmos::nc_method_status::method_not_implemented}, U("not implemented")); } - - return nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); } - } - // Gets the lost packet counters - web::json::value get_lost_packet_counters(nmos::resources& /*resources*/, const nmos::resource& /*resource*/, const web::json::value& /*arguments*/, bool is_deprecated, get_packet_counters_handler get_lost_packet_counters, slog::base_gate& gate) - { - slog::log(gate, SLOG_FLF) << "Gets the lost packet counters"; + // Gets the lost packet counters + web::json::value get_lost_packet_counters(nmos::resources& /*resources*/, const nmos::resource& /*resource*/, const web::json::value& /*arguments*/, bool is_deprecated, get_packet_counters_handler get_lost_packet_counters, slog::base_gate& gate) + { + slog::log(gate, SLOG_FLF) << "Gets the lost packet counters"; - return details::get_packet_counters(is_deprecated, get_lost_packet_counters); - } + return details::get_packet_counters(is_deprecated, get_lost_packet_counters); + } - // Gets the late packet counters - web::json::value get_late_packet_counters(nmos::resources& /*resources*/, const nmos::resource& /*resource*/, const web::json::value& /*arguments*/, bool is_deprecated, get_packet_counters_handler get_late_packet_counters, slog::base_gate& gate) - { - slog::log(gate, SLOG_FLF) << "Gets the late packet counters"; + // Gets the late packet counters + web::json::value get_late_packet_counters(nmos::resources& /*resources*/, const nmos::resource& /*resource*/, const web::json::value& /*arguments*/, bool is_deprecated, get_packet_counters_handler get_late_packet_counters, slog::base_gate& gate) + { + slog::log(gate, SLOG_FLF) << "Gets the late packet counters"; - return details::get_packet_counters(is_deprecated, get_late_packet_counters); - } + return details::get_packet_counters(is_deprecated, get_late_packet_counters); + } - // Resets the packet counters and messages - web::json::value reset_monitor(nmos::resources& resources, const nmos::resource& resource, const web::json::value&, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, control_protocol_property_changed_handler property_changed, reset_monitor_handler reset_monitor, slog::base_gate& gate) - { - slog::log(gate, SLOG_FLF) << "Resets the packet counters"; - - const std::vector> receiver_property_values = { - std::pair(nc_receiver_monitor_connection_status_transition_counter_property_id, web::json::value::number(0)), - std::pair(nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(0)), - std::pair(nc_receiver_monitor_link_status_transition_counter_property_id, web::json::value::number(0)), - std::pair(nc_receiver_monitor_stream_status_transition_counter_property_id, web::json::value::number(0)), - std::pair(nc_receiver_monitor_connection_status_message_property_id, web::json::value::null()), - std::pair(nc_receiver_monitor_external_synchronization_status_message_property_id, web::json::value::null()), - std::pair(nc_receiver_monitor_link_status_message_property_id, web::json::value::null()), - std::pair(nc_receiver_monitor_stream_status_message_property_id, web::json::value::null()), - std::pair(nc_status_monitor_overall_status_message_property_id, web::json::value::null()), - }; - - const std::vector> sender_property_values = { - std::pair(nc_sender_monitor_transmission_status_transition_counter_property_id, web::json::value::number(0)), - std::pair(nc_sender_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(0)), - std::pair(nc_sender_monitor_link_status_transition_counter_property_id, web::json::value::number(0)), - std::pair(nc_sender_monitor_essence_status_transition_counter_property_id, web::json::value::number(0)), - std::pair(nc_sender_monitor_transmission_status_message_property_id, web::json::value::null()), - std::pair(nc_sender_monitor_external_synchronization_status_message_property_id, web::json::value::null()), - std::pair(nc_sender_monitor_link_status_message_property_id, web::json::value::null()), - std::pair(nc_sender_monitor_essence_status_message_property_id, web::json::value::null()), - std::pair(nc_status_monitor_overall_status_message_property_id, web::json::value::null()), - }; - - const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); - - // reset all counters - const std::vector> property_values = nmos::nc::is_sender_monitor(class_id) ? sender_property_values : receiver_property_values; - - for (const auto& property_value : property_values) + // Resets the packet counters and messages + web::json::value reset_monitor(nmos::resources& resources, const nmos::resource& resource, const web::json::value&, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, control_protocol_property_changed_handler property_changed, reset_monitor_handler reset_monitor, slog::base_gate& gate) { - const auto& property = nc::find_property_descriptor(property_value.first, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); - if (!property.is_null()) + slog::log(gate, SLOG_FLF) << "Resets the packet counters"; + + const std::vector> receiver_property_values = { + std::pair(nc_receiver_monitor_connection_status_transition_counter_property_id, web::json::value::number(0)), + std::pair(nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(0)), + std::pair(nc_receiver_monitor_link_status_transition_counter_property_id, web::json::value::number(0)), + std::pair(nc_receiver_monitor_stream_status_transition_counter_property_id, web::json::value::number(0)), + std::pair(nc_receiver_monitor_connection_status_message_property_id, web::json::value::null()), + std::pair(nc_receiver_monitor_external_synchronization_status_message_property_id, web::json::value::null()), + std::pair(nc_receiver_monitor_link_status_message_property_id, web::json::value::null()), + std::pair(nc_receiver_monitor_stream_status_message_property_id, web::json::value::null()), + std::pair(nc_status_monitor_overall_status_message_property_id, web::json::value::null()), + }; + + const std::vector> sender_property_values = { + std::pair(nc_sender_monitor_transmission_status_transition_counter_property_id, web::json::value::number(0)), + std::pair(nc_sender_monitor_external_synchronization_status_transition_counter_property_id, web::json::value::number(0)), + std::pair(nc_sender_monitor_link_status_transition_counter_property_id, web::json::value::number(0)), + std::pair(nc_sender_monitor_essence_status_transition_counter_property_id, web::json::value::number(0)), + std::pair(nc_sender_monitor_transmission_status_message_property_id, web::json::value::null()), + std::pair(nc_sender_monitor_external_synchronization_status_message_property_id, web::json::value::null()), + std::pair(nc_sender_monitor_link_status_message_property_id, web::json::value::null()), + std::pair(nc_sender_monitor_essence_status_message_property_id, web::json::value::null()), + std::pair(nc_status_monitor_overall_status_message_property_id, web::json::value::null()), + }; + + const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + + // reset all counters + const std::vector> property_values = nmos::nc::is_sender_monitor(class_id) ? sender_property_values : receiver_property_values; + + for (const auto& property_value : property_values) { - try + const auto& property = nc::find_property_descriptor(property_value.first, nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + if (!property.is_null()) { - // update property - nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) + try { - resource.data[nmos::fields::nc::name(property)] = property_value.second; - - // do notification that the specified property has changed - if (property_changed) + // update property + nc::modify_resource(resources, resource.id, [&](nmos::resource& resource) { - property_changed(resource, nmos::fields::nc::name(property), -1); - } + resource.data[nmos::fields::nc::name(property)] = property_value.second; - }, make_property_changed_event(nmos::fields::nc::oid(resource.data), { { property_value.first, nc_property_change_type::type::value_changed, property_value.second } })); - } - catch (const nmos::control_protocol_exception& e) - { - utility::ostringstream_t ss; - ss << "Reset counters: " << details::make_nc_property_id(property_value.first).serialize() << " error: " << e.what(); - slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({ nc_method_status::parameter_error }, ss.str()); + // do notification that the specified property has changed + if (property_changed) + { + property_changed(resource, nmos::fields::nc::name(property), -1); + } + + }, make_property_changed_event(nmos::fields::nc::oid(resource.data), {{property_value.first, nc_property_change_type::type::value_changed, property_value.second}})); + } + catch (const nmos::control_protocol_exception& e) + { + utility::ostringstream_t ss; + ss << "Reset counters: " << nmos::details::make_nc_property_id(property_value.first).serialize() << " error: " << e.what(); + slog::log(gate, SLOG_FLF) << ss.str(); + return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + } } } - } - if (reset_monitor) - { - reset_monitor(); - } + if (reset_monitor) + { + reset_monitor(); + } - return nmos::details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok }); + return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}); + } } } diff --git a/Development/nmos/control_protocol_methods.h b/Development/nmos/control_protocol_methods.h index a855f28db..b4f80ed54 100644 --- a/Development/nmos/control_protocol_methods.h +++ b/Development/nmos/control_protocol_methods.h @@ -11,45 +11,47 @@ namespace slog namespace nmos { - // NcObject methods implementation - // Get property value - web::json::value get(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set property value - web::json::value set(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - // Get sequence item - web::json::value get_sequence_item(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Set sequence item - web::json::value set_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - // Add item to sequence - web::json::value add_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - // Delete sequence item - web::json::value remove_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); - // Get sequence length - web::json::value get_sequence_length(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + namespace nc + { + // NcObject methods implementation + // Get property value + web::json::value get(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set property value + web::json::value set(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + // Get sequence item + web::json::value get_sequence_item(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Set sequence item + web::json::value set_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + // Add item to sequence + web::json::value add_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + // Delete sequence item + web::json::value remove_sequence_item(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, control_protocol_property_changed_handler property_changed, slog::base_gate& gate); + // Get sequence length + web::json::value get_sequence_length(const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // NcBlock methods implementation - // Get descriptors of members of the block - web::json::value get_member_descriptors(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate); - // Finds member(s) by path - web::json::value find_members_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate); - // Finds members with given role name or fragment - web::json::value find_members_by_role(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate); - // Finds members with given class id - web::json::value find_members_by_class_id(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate); + // NcBlock methods implementation + // Get descriptors of members of the block + web::json::value get_member_descriptors(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate); + // Finds member(s) by path + web::json::value find_members_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate); + // Finds members with given role name or fragment + web::json::value find_members_by_role(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate); + // Finds members with given class id + web::json::value find_members_by_class_id(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate); - // NcClassManager methods implementation - // Get a single class descriptor - web::json::value get_control_class(const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); - // Get a single datatype descriptor - web::json::value get_datatype(const web::json::value& arguments, bool is_deprecated, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype, slog::base_gate& gate); + // NcClassManager methods implementation + // Get a single class descriptor + web::json::value get_control_class(const web::json::value& arguments, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate); + // Get a single datatype descriptor + web::json::value get_datatype(const web::json::value& arguments, bool is_deprecated, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype, slog::base_gate& gate); // NcReceiverMonitor methods implementation // Gets the lost packet counters - web::json::value get_lost_packet_counters(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_packet_counters_handler get_lost_packet_counters, slog::base_gate& gate); - // Gets the last packet counters - web::json::value get_late_packet_counters(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_packet_counters_handler get_last_packet_counters, slog::base_gate& gate); - // Resets the packet counters and messages - web::json::value reset_monitor(nmos::resources& resources, const nmos::resource& resource, const web::json::value&, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, control_protocol_property_changed_handler property_changed, reset_monitor_handler reset_monitor, slog::base_gate& gate); + web::json::value get_lost_packet_counters(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_packet_counters_handler get_lost_packet_counters, slog::base_gate& gate); + // Gets the last packet counters + web::json::value get_late_packet_counters(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_packet_counters_handler get_last_packet_counters, slog::base_gate& gate); + // Resets the packet counters and messages + web::json::value reset_monitor(nmos::resources& resources, const nmos::resource& resource, const web::json::value&, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, control_protocol_property_changed_handler property_changed, reset_monitor_handler reset_monitor, slog::base_gate& gate); + } } - #endif diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index e22d62fae..836d855d6 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -93,91 +93,91 @@ namespace nmos { return [get_control_protocol_class_descriptor](nmos::resources&, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return get(resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); + return nc::get(resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_set_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed) { return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return set(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate); + return nc::set(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_get_sequence_item_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { return [get_control_protocol_class_descriptor](nmos::resources&, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return get_sequence_item(resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); + return nc::get_sequence_item(resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_set_sequence_item_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed) { return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return set_sequence_item(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate); + return nc::set_sequence_item(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_add_sequence_item_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, control_protocol_property_changed_handler property_changed) { return [get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return add_sequence_item(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate); + return nc::add_sequence_item(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, property_changed, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_remove_sequence_item_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, control_protocol_property_changed_handler property_changed) { return [get_control_protocol_class_descriptor, property_changed](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return remove_sequence_item(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, property_changed, gate); + return nc::remove_sequence_item(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, property_changed, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_get_sequence_length_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { return [get_control_protocol_class_descriptor](nmos::resources&, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return get_sequence_length(resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); + return nc::get_sequence_length(resource, arguments, is_deprecated, get_control_protocol_class_descriptor, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_get_member_descriptors_handler() { return [](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return get_member_descriptors(resources, resource, arguments, is_deprecated, gate); + return nc::get_member_descriptors(resources, resource, arguments, is_deprecated, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_find_members_by_path_handler() { return [](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return find_members_by_path(resources, resource, arguments, is_deprecated, gate); + return nc::find_members_by_path(resources, resource, arguments, is_deprecated, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_find_members_by_role_handler() { return [](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return find_members_by_role(resources, resource, arguments, is_deprecated, gate); + return nc::find_members_by_role(resources, resource, arguments, is_deprecated, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_find_members_by_class_id_handler() { return [](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return find_members_by_class_id(resources, resource, arguments, is_deprecated, gate); + return nc::find_members_by_class_id(resources, resource, arguments, is_deprecated, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_get_control_class_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { return [get_control_protocol_class_descriptor](nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return get_control_class(arguments, is_deprecated, get_control_protocol_class_descriptor, gate); + return nc::get_control_class(arguments, is_deprecated, get_control_protocol_class_descriptor, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_get_datatype_handler(get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { return [get_control_protocol_datatype_descriptor](nmos::resources&, const nmos::resource&, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return get_datatype(arguments, is_deprecated, get_control_protocol_datatype_descriptor, gate); + return nc::get_datatype(arguments, is_deprecated, get_control_protocol_datatype_descriptor, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_get_properties_by_path_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, create_validation_fingerprint_handler create_validation_fingerprint) @@ -248,21 +248,21 @@ namespace nmos { return [get_lost_packet_counters](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return nmos::get_lost_packet_counters(resources, resource, arguments, is_deprecated, get_lost_packet_counters, gate); + return nc::get_lost_packet_counters(resources, resource, arguments, is_deprecated, get_lost_packet_counters, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_get_late_packet_counters_handler(get_packet_counters_handler get_late_packet_counters) { return [get_late_packet_counters](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return nmos::get_late_packet_counters(resources, resource, arguments, is_deprecated, get_late_packet_counters, gate); + return nc::get_late_packet_counters(resources, resource, arguments, is_deprecated, get_late_packet_counters, gate); }; } nmos::experimental::control_protocol_method_handler make_nc_reset_monitor_handler(get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, control_protocol_property_changed_handler property_changed, reset_monitor_handler reset_monitor) { return [get_control_protocol_class_descriptor, property_changed, reset_monitor](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { - return nmos::reset_monitor(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, property_changed, reset_monitor, gate); + return nc::reset_monitor(resources, resource, arguments, is_deprecated, get_control_protocol_class_descriptor, property_changed, reset_monitor, gate); }; } } diff --git a/Development/nmos/test/control_protocol_methods_test.cpp b/Development/nmos/test/control_protocol_methods_test.cpp index df0a3746f..c235f0496 100644 --- a/Development/nmos/test/control_protocol_methods_test.cpp +++ b/Development/nmos/test/control_protocol_methods_test.cpp @@ -114,7 +114,7 @@ BST_TEST_CASE(testRemoveSequenceItem) auto resource = nmos::find_resource(resources, receivers_id); BST_CHECK_NE(resources.end(), resource); - auto result = nmos::remove_sequence_item(resources, *resource, arguments, false, get_control_protocol_class_descriptor, property_changed, gate); + auto result = nmos::nc::remove_sequence_item(resources, *resource, arguments, false, get_control_protocol_class_descriptor, property_changed, gate); // Expect read only error, and for property changed not to be called BST_CHECK_EQUAL(false, property_changed_called); @@ -137,7 +137,7 @@ BST_TEST_CASE(testRemoveSequenceItem) auto resource = nmos::find_resource(resources, writable_sequence_id); BST_CHECK_NE(resources.end(), resource); - auto result = nmos::remove_sequence_item(resources, *resource, arguments, false, get_control_protocol_class_descriptor, property_changed, gate); + auto result = nmos::nc::remove_sequence_item(resources, *resource, arguments, false, get_control_protocol_class_descriptor, property_changed, gate); // Expect success, and property changed event BST_CHECK_EQUAL(true, property_changed_called); From d91304b8580d3749b49e6baa1025416fb5ecf13a Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 12 Sep 2025 12:16:46 +0100 Subject: [PATCH 243/250] Move control_protocol_resource functions into nc namespace --- .../nmos-cpp-node/node_implementation.cpp | 62 +- Development/nmos/configuration_api.cpp | 72 +- Development/nmos/configuration_methods.cpp | 20 +- Development/nmos/configuration_resources.cpp | 8 +- Development/nmos/configuration_utils.cpp | 82 +- .../nmos/control_protocol_behaviour.cpp | 4 +- .../nmos/control_protocol_handlers.cpp | 4 +- Development/nmos/control_protocol_methods.cpp | 146 +- .../nmos/control_protocol_resource.cpp | 4203 +++++++++-------- Development/nmos/control_protocol_resource.h | 882 ++-- .../nmos/control_protocol_resources.cpp | 22 +- Development/nmos/control_protocol_state.cpp | 262 +- Development/nmos/control_protocol_utils.cpp | 32 +- Development/nmos/control_protocol_ws_api.cpp | 22 +- .../nmos/test/configuration_methods_test.cpp | 6 +- .../nmos/test/configuration_utils_test.cpp | 390 +- .../test/control_protocol_methods_test.cpp | 2 +- .../nmos/test/control_protocol_test.cpp | 192 +- .../nmos/test/control_protocol_utils_test.cpp | 12 +- 19 files changed, 3214 insertions(+), 3209 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index d34fa0931..534ceb424 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -960,7 +960,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // helper function to create Gain control instance auto make_gain_control = [&gain_value, &gain_control_class_id](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, float gain) { - auto data = nmos::details::make_nc_worker(gain_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); + auto data = nmos::nc::details::make_nc_worker(gain_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); data[gain_value] = value::number(gain); return nmos::control_protocol_resource{ nmos::is12_versions::v1_0, nmos::types::nc_worker, std::move(data), true }; @@ -996,17 +996,17 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr }; { // following constraints are used for the example control class level 0 datatype, level 1 property constraints and the method parameters constraints - auto make_string_example_argument_constraints = []() {return nmos::details::make_nc_parameter_constraints_string(10, U("^[a-z]+$")); }; - auto make_number_example_argument_constraints = []() {return nmos::details::make_nc_parameter_constraints_number(0, 1000, 1); }; + auto make_string_example_argument_constraints = []() {return nmos::nc::details::make_nc_parameter_constraints_string(10, U("^[a-z]+$")); }; + auto make_number_example_argument_constraints = []() {return nmos::nc::details::make_nc_parameter_constraints_number(0, 1000, 1); }; // Example control class property descriptors std::vector example_control_property_descriptors = { nmos::experimental::make_control_class_property_descriptor(U("Example enum property"), { 3, 1 }, enum_property, U("ExampleEnum")), // create "Example string property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use nmos::details::make_nc_parameter_constraints_string to create property constraints + // use nmos::nc::details::make_nc_parameter_constraints_string to create property constraints nmos::experimental::make_control_class_property_descriptor(U("Example string property"), { 3, 2 }, string_property, U("NcString"), false, false, false, false, make_string_example_argument_constraints()), // create "Example numeric property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use nmos::details::make_nc_parameter_constraints_number to create property constraints + // use nmos::nc::details::make_nc_parameter_constraints_number to create property constraints nmos::experimental::make_control_class_property_descriptor(U("Example numeric property"), { 3, 3 }, number_property, U("NcUint64"), false, false, false, false, make_number_example_argument_constraints()), nmos::experimental::make_control_class_property_descriptor(U("Example deprecated numeric property"), { 3, 4 }, deprecated_number_property, U("NcUint64"), false, false, false, true, make_number_example_argument_constraints()), nmos::experimental::make_control_class_property_descriptor(U("Example boolean property"), { 3, 5 }, boolean_property, U("NcBoolean")), @@ -1015,12 +1015,12 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr nmos::experimental::make_control_class_property_descriptor(U("Example method simple args invoke counter"), { 3, 8 }, method_simple_args_count, U("NcUint64"), true), nmos::experimental::make_control_class_property_descriptor(U("Example method obj arg invoke counter"), { 3, 9 }, method_object_arg_count, U("NcUint64"), true), // create "Example sequence string property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use nmos::details::make_nc_parameter_constraints_string to create sequence property constraints + // use nmos::nc::details::make_nc_parameter_constraints_string to create sequence property constraints nmos::experimental::make_control_class_property_descriptor(U("Example string sequence property"), { 3, 10 }, string_sequence, U("NcString"), false, false, true, false, make_string_example_argument_constraints()), nmos::experimental::make_control_class_property_descriptor(U("Example boolean sequence property"), { 3, 11 }, boolean_sequence, U("NcBoolean"), false, false, true), nmos::experimental::make_control_class_property_descriptor(U("Example enum sequence property"), { 3, 12 }, enum_sequence, U("ExampleEnum"), false, false, true), // create "Example sequence numeric property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use nmos::details::make_nc_parameter_constraints_number to create sequence property constraints + // use nmos::nc::details::make_nc_parameter_constraints_number to create sequence property constraints nmos::experimental::make_control_class_property_descriptor(U("Example number sequence property"), { 3, 13 }, number_sequence, U("NcUint64"), false, false, true, false, make_number_example_argument_constraints()), nmos::experimental::make_control_class_property_descriptor(U("Example object sequence property"), { 3, 14 }, object_sequence, U("ExampleDataType"), false, false, true) }; @@ -1031,7 +1031,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr slog::log(gate, SLOG_FLF) << "Executing the example method with no arguments"; - return nmos::details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); + return nmos::nc::details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); }; auto example_method_with_simple_args = [](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { @@ -1040,7 +1040,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr slog::log(gate, SLOG_FLF) << "Executing the example method with simple arguments: " << arguments.serialize(); - return nmos::details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); + return nmos::nc::details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); }; auto example_method_with_object_args = [](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { @@ -1049,7 +1049,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr slog::log(gate, SLOG_FLF) << "Executing the example method with object argument: " << arguments.serialize(); - return nmos::details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); + return nmos::nc::details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); }; // Example control class method descriptors std::vector example_control_method_descriptors = @@ -1085,32 +1085,32 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr using web::json::value; auto items = value::array(); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Undefined"), U("Undefined"), example_enum::Undefined)); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Alpha"), U("Alpha"), example_enum::Alpha)); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Beta"), U("Beta"), example_enum::Beta)); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Gamma"), U("Gamma"), example_enum::Gamma)); - return nmos::details::make_nc_datatype_descriptor_enum(U("Example enum datatype"), U("ExampleEnum"), items, value::null()); + web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Undefined"), U("Undefined"), example_enum::Undefined)); + web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Alpha"), U("Alpha"), example_enum::Alpha)); + web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Beta"), U("Beta"), example_enum::Beta)); + web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Gamma"), U("Gamma"), example_enum::Gamma)); + return nmos::nc::details::make_nc_datatype_descriptor_enum(U("Example enum datatype"), U("ExampleEnum"), items, value::null()); }; auto make_example_datatype_datatype = [&]() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Enum property example"), enum_property, U("ExampleEnum"), false, false, value::null())); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Enum property example"), enum_property, U("ExampleEnum"), false, false, value::null())); { // level 0: datatype constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use nmos::details::make_nc_parameter_constraints_string to create datatype constraints + // use nmos::nc::details::make_nc_parameter_constraints_string to create datatype constraints value datatype_constraints = make_string_example_argument_constraints(); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("String property example"), string_property, U("NcString"), false, false, datatype_constraints)); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("String property example"), string_property, U("NcString"), false, false, datatype_constraints)); } { // level 0: datatype constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use nmos::details::make_nc_parameter_constraints_number to create datatype constraints + // use nmos::nc::details::make_nc_parameter_constraints_number to create datatype constraints value datatype_constraints = make_number_example_argument_constraints(); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Number property example"), number_property, U("NcUint64"), false, false, datatype_constraints)); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Number property example"), number_property, U("NcUint64"), false, false, datatype_constraints)); } - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Boolean property example"), boolean_property, U("NcBoolean"), false, false, value::null())); - return nmos::details::make_nc_datatype_descriptor_struct(U("Example data type"), U("ExampleDataType"), fields, value::null()); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Boolean property example"), boolean_property, U("NcBoolean"), false, false, value::null())); + return nmos::nc::details::make_nc_datatype_descriptor_struct(U("Example data type"), U("ExampleDataType"), fields, value::null()); }; control_protocol_state.insert(nmos::experimental::datatype_descriptor{ make_example_enum_datatype() }); control_protocol_state.insert(nmos::experimental::datatype_descriptor{ make_example_datatype_datatype() }); @@ -1146,7 +1146,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr std::vector number_sequence_, std::vector object_sequence_) { - auto data = nmos::details::make_nc_worker(example_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); + auto data = nmos::nc::details::make_nc_worker(example_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); data[enum_property] = value::number(enum_property_); data[string_property] = value::string(string_property_); data[number_property] = value::number(number_property_); @@ -1205,7 +1205,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // helper function to create Temperature Sensor control instance auto make_temperature_sensor = [&temperature, &unit, temperature_sensor_control_class_id](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, float temperature_, const utility::string_t& unit_) { - auto data = nmos::details::make_nc_worker(temperature_sensor_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); + auto data = nmos::nc::details::make_nc_worker(temperature_sensor_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); data[temperature] = value::number(temperature_); data[unit] = value::string(unit_); @@ -1253,8 +1253,8 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // specify the level 2: runtime constraints, see https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html // use of make_nc_property_constraints_string and make_nc_property_constraints_number to create runtime constraints value_of({ - { nmos::details::make_nc_property_constraints_string({3, 2}, 5, U("^[a-z]+$")) }, - { nmos::details::make_nc_property_constraints_number({3, 3}, 10, 100, 2) } + { nmos::nc::details::make_nc_property_constraints_string({3, 2}, 5, U("^[a-z]+$")) }, + { nmos::nc::details::make_nc_property_constraints_number({3, 3}, 10, 100, 2) } }), example_enum::Undefined, U("test"), @@ -1294,7 +1294,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr utility::ostringstream_t role; role << U("receiver-monitor-") << ++count; const auto& receiver = nmos::find_resource(model.node_resources, receiver_id); - auto receiver_monitor = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, receiver_id}) } })); + auto receiver_monitor = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, receiver_id}) } })); // optionally indicate dependencies within the device model nmos::set_object_dependency_paths(receiver_monitor, {{U("root"), U("receivers")}}); // add receiver-monitor to root-block @@ -1315,7 +1315,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr utility::ostringstream_t role; role << U("sender-monitor-") << ++count; const auto& sender = nmos::find_resource(model.node_resources, sender_id); - const auto sender_monitor = nmos::make_sender_monitor(++oid, true, nmos::root_block_oid, role.str(), nmos::fields::label(sender->data), nmos::fields::description(sender->data), value_of({ { nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::sender, sender_id}) } })); + const auto sender_monitor = nmos::make_sender_monitor(++oid, true, nmos::root_block_oid, role.str(), nmos::fields::label(sender->data), nmos::fields::description(sender->data), value_of({ { nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::sender, sender_id}) } })); // add sender-monitor to root-block nmos::nc::push_back(root_block, sender_monitor); @@ -1443,12 +1443,12 @@ void node_implementation_run(nmos::node_model& model, nmos::experimental::contro auto found = nmos::find_resource_if(resources, nmos::types::nc_worker, [&temperature_sensor_control_class_id](const nmos::resource& resource) { - return temperature_sensor_control_class_id == nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + return temperature_sensor_control_class_id == nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); }); if (resources.end() != found) { - const auto property_changed_event = nmos::make_property_changed_event(nmos::fields::nc::oid(found->data), + const auto property_changed_event = nmos::nc::make_property_changed_event(nmos::fields::nc::oid(found->data), { { {3, 1}, nmos::nc_property_change_type::type::value_changed, web::json::value(temp.scaled_value()) } // hmm, maybe pull out {3, 1} temperature property id to impl namespace }); @@ -2056,7 +2056,7 @@ nmos::create_device_model_object_handler make_create_device_model_object_handler const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); // In the case of validate = true, the object created will not be added to the device model, but it's values will be checked against the backup dataset - return nmos::make_receiver_monitor(oid, true, owner, role, user_label, U(""), web::json::value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})} })); + return nmos::make_receiver_monitor(oid, true, owner, role, user_label, U(""), web::json::value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})} })); }; } diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index c0280d207..499e044dc 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -70,7 +70,7 @@ namespace nmos role_paths.insert(role_path + U("/")); // get members on all NcBlock(s) - if (nmos::nc::is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + if (nmos::nc::is_block(nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(member); @@ -216,7 +216,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -236,7 +236,7 @@ namespace nmos { std::set properties_routes; - auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); + auto class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); while (!class_id.empty()) { const auto& control_class = get_control_protocol_class_descriptor(class_id); @@ -257,7 +257,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -277,7 +277,7 @@ namespace nmos { std::set methods_routes; - auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); + auto class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); while (!class_id.empty()) { const auto& control_class = get_control_protocol_class_descriptor(class_id); @@ -305,7 +305,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -323,7 +323,7 @@ namespace nmos const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { - nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); + nc_class_id class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); if (!class_id.empty()) { @@ -353,17 +353,17 @@ namespace nmos } auto class_descriptor = fixed_role.is_null() - ? details::make_nc_class_descriptor(description, class_id, name, property_descriptors, method_descriptors, event_descriptors) - : details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), property_descriptors, method_descriptors, event_descriptors); + ? nc::details::make_nc_class_descriptor(description, class_id, name, property_descriptors, method_descriptors, event_descriptors) + : nc::details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), property_descriptors, method_descriptors, event_descriptors); - auto method_result = details::make_nc_method_result({ nmos::nc_method_status::ok }, class_descriptor); + auto method_result = nc::details::make_nc_method_result({ nmos::nc_method_status::ok }, class_descriptor); set_reply(res, status_codes::OK, method_result); } } else { // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -383,11 +383,11 @@ namespace nmos if (resources.end() != resource) { // find the relevant nc_property_descriptor - const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); + const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::property_not_implemented }, U("Not Found; ") + property_id); + auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::property_not_implemented }, U("Not Found; ") + property_id); set_reply(res, status_codes::NotFound, method_result); } else @@ -398,7 +398,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -418,7 +418,7 @@ namespace nmos if (resources.end() != resource) { // find the relevant nc_property_descriptor - const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); + const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); const auto& property_type = nmos::fields::nc::type_name(property_descriptor); auto datatype_descriptor = nc::details::get_datatype_descriptor(value::string(property_type), get_control_protocol_datatype_descriptor); @@ -442,19 +442,19 @@ namespace nmos if (property_descriptor.is_null()) { // property not found - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::property_not_implemented }, U("Not Found; ") + property_id); + auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::property_not_implemented }, U("Not Found; ") + property_id); set_reply(res, status_codes::NotFound, method_result); } else { - auto method_result = details::make_nc_method_result({ nmos::nc_method_status::ok }, datatype_descriptor); + auto method_result = nc::details::make_nc_method_result({ nmos::nc_method_status::ok }, datatype_descriptor); set_reply(res, status_codes::OK, method_result); } } else { // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -474,7 +474,7 @@ namespace nmos if (resources.end() != resource) { auto arguments = value_of({ - { nmos::fields::nc::id, details::make_nc_property_id(details::parse_formatted_property_id(property_id))}, + { nmos::fields::nc::id, nc::details::make_nc_property_id(details::parse_formatted_property_id(property_id))}, }); auto result = nc::get(*resource, arguments, false, get_control_protocol_class_descriptor, gate_); @@ -485,7 +485,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -514,7 +514,7 @@ namespace nmos const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { - auto method = get_control_protocol_method_descriptor(details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), details::parse_formatted_method_id(method_id)); + auto method = get_control_protocol_method_descriptor(nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), details::parse_formatted_method_id(method_id)); auto& nc_method_descriptor = method.first; auto& control_method_handler = method.second; web::http::status_code code{ status_codes::BadRequest }; @@ -541,7 +541,7 @@ namespace nmos // invalid arguments utility::stringstream_t ss; ss << U("invalid argument: ") << arguments.serialize() << " error: " << e.what(); - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); code = status_codes::BadRequest; } @@ -552,7 +552,7 @@ namespace nmos utility::stringstream_t ss; ss << U("unsupported method_id: ") << method_id << U(" for control class class_id: ") << resource->data.at(nmos::fields::nc::class_id).serialize(); - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, ss.str()); + method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, ss.str()); code = status_codes::NotFound; } @@ -561,7 +561,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -591,17 +591,17 @@ namespace nmos if (resources.end() != resource) { // find the relevant nc_property_descriptor - const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); + const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { // property not found - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + property_id); + auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + property_id); set_reply(res, status_codes::NotFound, method_result); } else { auto arguments = value_of({ - { nmos::fields::nc::id, details::make_nc_property_id(details::parse_formatted_property_id(property_id))}, + { nmos::fields::nc::id, nc::details::make_nc_property_id(details::parse_formatted_property_id(property_id))}, { nmos::fields::nc::value, nmos::fields::nc::value(body)} }); @@ -616,7 +616,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -656,7 +656,7 @@ namespace nmos // invalid arguments utility::stringstream_t ss; ss << U("parameter error: ") << e.what(); - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); code = status_codes::BadRequest; } @@ -665,7 +665,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -711,7 +711,7 @@ namespace nmos // invalid arguments utility::stringstream_t ss; ss << U("parameter error: ") << e.what(); - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); code = status_codes::BadRequest; } @@ -720,7 +720,7 @@ namespace nmos // JSON validation error utility::stringstream_t ss; ss << U("JSON validation error: ") << e.what(); - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); code = status_codes::BadRequest; } @@ -729,7 +729,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } return true; @@ -773,7 +773,7 @@ namespace nmos // invalid arguments utility::stringstream_t ss; ss << U("parameter error: ") << e.what(); - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); code = status_codes::BadRequest; } @@ -782,7 +782,7 @@ namespace nmos // JSON validation error utility::stringstream_t ss; ss << U("JSON validation error: ") << e.what(); - method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); code = status_codes::BadRequest; } @@ -791,7 +791,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index e16e34341..1a3a3090a 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -19,7 +19,7 @@ namespace nmos value property_holders = value::array(); - nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + nmos::nc_class_id class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); // make NcPropertyHolder objects while (!class_id.empty()) @@ -29,7 +29,7 @@ namespace nmos for (const auto& property_descriptor : control_class_descriptor.property_descriptors.as_array()) { const auto descriptor = include_descriptors ? property_descriptor : value::null(); - value property_holder = nmos::details::make_nc_property_holder(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)), descriptor, resource.data.at(nmos::fields::nc::name(property_descriptor))); + value property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)), descriptor, resource.data.at(nmos::fields::nc::name(property_descriptor))); web::json::push_back(property_holders, property_holder); } @@ -55,12 +55,12 @@ namespace nmos const auto& dependency_paths = nmos::fields::nc::dependency_paths(resource.data); const auto& allowed_member_classes = nmos::fields::nc::allowed_members_classes(resource.data); - auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path, property_holders, dependency_paths, allowed_member_classes, nmos::fields::nc::is_rebuildable(resource.data)); + auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path, property_holders, dependency_paths, allowed_member_classes, nmos::fields::nc::is_rebuildable(resource.data)); web::json::push_back(object_properties_holders, object_properties_holder); // Recurse into members...if we want to...and the object has them - if (recurse && nmos::nc::is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)))) + if (recurse && nmos::nc::is_block(nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)))) { if (resource.data.has_field(nmos::fields::nc::members)) { @@ -95,9 +95,9 @@ namespace nmos validation_fingerprint = create_validation_fingerprint(resources, resource); } - auto bulk_properties_holder = nmos::details::make_nc_bulk_properties_holder(validation_fingerprint, object_properties_holders); + auto bulk_properties_holder = nmos::nc::details::make_nc_bulk_properties_holder(validation_fingerprint, object_properties_holders); - return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, bulk_properties_holder); + return nmos::nc::details::make_nc_method_result({ nmos::nc_method_status::ok }, bulk_properties_holder); } web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, nmos::nc_restore_mode::restore_mode restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) @@ -108,14 +108,14 @@ namespace nmos if (!validate_validation_fingerprint(resources, resource, validation_fingerprint.c_str())) { - return nmos::details::make_nc_method_result_error({ nmos::nc_method_status::invalid_request }, U("Invalid validation fingerprint")); + return nmos::nc::details::make_nc_method_result_error({ nmos::nc_method_status::invalid_request }, U("Invalid validation fingerprint")); } } const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); const auto object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); - return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); + return nmos::nc::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, nmos::nc_restore_mode::restore_mode restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) @@ -126,13 +126,13 @@ namespace nmos if (!validate_validation_fingerprint(resources, resource, validation_fingerprint.c_str())) { - return nmos::details::make_nc_method_result_error({ nmos::nc_method_status::invalid_request }, U("Invalid validation fingerprint")); + return nmos::nc::details::make_nc_method_result_error({ nmos::nc_method_status::invalid_request }, U("Invalid validation fingerprint")); } } const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); const auto object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); - return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); + return nmos::nc::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } } \ No newline at end of file diff --git a/Development/nmos/configuration_resources.cpp b/Development/nmos/configuration_resources.cpp index 949fa3fd9..0df84975d 100644 --- a/Development/nmos/configuration_resources.cpp +++ b/Development/nmos/configuration_resources.cpp @@ -8,21 +8,21 @@ namespace nmos { web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::array& notices, const utility::string_t& status_message) { - return details::make_nc_object_properties_set_validation(role_path, status, notices, web::json::value::string(status_message)); + return nc::details::make_nc_object_properties_set_validation(role_path, status, notices, web::json::value::string(status_message)); } web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::array& notices) { - return details::make_nc_object_properties_set_validation(role_path, status, notices, web::json::value::null()); + return nc::details::make_nc_object_properties_set_validation(role_path, status, notices, web::json::value::null()); } web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status) { - return details::make_nc_object_properties_set_validation(role_path, status, web::json::value::array().as_array(), web::json::value::null()); + return nc::details::make_nc_object_properties_set_validation(role_path, status, web::json::value::array().as_array(), web::json::value::null()); } web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const utility::string_t& status_message) { - return details::make_nc_object_properties_set_validation(role_path, status, web::json::value::array().as_array(), web::json::value::string(status_message)); + return nc::details::make_nc_object_properties_set_validation(role_path, status, web::json::value::array().as_array(), web::json::value::string(status_message)); } } diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 12bac2b7e..9beb71702 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -17,13 +17,13 @@ namespace nmos { bool is_property_value_valid(web::json::value& property_restore_notices, const web::json::value& property_value, const web::json::value& property_descriptor, nmos::nc_restore_mode::restore_mode restore_mode, bool is_rebuildable) { - const nmos::nc_property_id& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)); + const nmos::nc_property_id& property_id = nc::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)); bool is_valid = true; // Only allow modification of read only properties when in Rebuild mode if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && restore_mode != nmos::nc_restore_mode::restore_mode::rebuild) { - const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::error, U("read only properties can not be modified in Modify restore mode.")); + const auto& property_restore_notice = nc::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::error, U("read only properties can not be modified in Modify restore mode.")); web::json::push_back(property_restore_notices, property_restore_notice); is_valid = false; } @@ -31,7 +31,7 @@ namespace nmos if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && !is_rebuildable) { - const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::error, U("object must be rebuildable to allow modification of read only properties.")); + const auto& property_restore_notice = nc::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::error, U("object must be rebuildable to allow modification of read only properties.")); web::json::push_back(property_restore_notices, property_restore_notice); is_valid = false; } @@ -43,7 +43,7 @@ namespace nmos { for (const auto& property_value : property_values) { - const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_id = nc::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); if (bool(nmos::fields::nc::is_read_only(property_descriptor))) @@ -56,7 +56,7 @@ namespace nmos web::json::value modify_device_model_object(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& target_object_properties_holder, nmos::nc_restore_mode::restore_mode restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list) { - const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + const auto& class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); auto object_properties_set_validation_values = web::json::value::array(); @@ -67,8 +67,8 @@ namespace nmos const auto& filtered_property_values = boost::copy_range>(nmos::fields::nc::values(target_object_properties_holder) | boost::adaptors::filtered([&property_restore_notices, &resource, class_id, get_control_protocol_class_descriptor, restore_mode](const web::json::value& property_value) { - const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); - const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); + const auto& property_id = nc::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_descriptor = nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); return resource.data.at(nmos::fields::nc::name(property_descriptor)) != nmos::fields::nc::value(property_value) && details::is_property_value_valid(property_restore_notices, property_value, property_descriptor, restore_mode, bool(nmos::fields::nc::is_rebuildable(resource.data))); @@ -82,7 +82,7 @@ namespace nmos const auto& read_only_property_values = boost::copy_range>(filtered_property_values | boost::adaptors::filtered([class_id, get_control_protocol_class_descriptor](const web::json::value& property_value) { - const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_id = nc::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); return nmos::fields::nc::is_read_only(property_descriptor); @@ -103,7 +103,7 @@ namespace nmos std::vector read_only_property_ids; for (const auto& property_value: read_only_property_values) { - const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_id = nc::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); // Don't include structural properties - changing these could break the device model if (property_id != nmos::nc_object_class_id_property_id && property_id != nmos::nc_object_oid_property_id && @@ -111,7 +111,7 @@ namespace nmos property_id != nmos::nc_object_owner_property_id && property_id != nmos::nc_object_role_property_id) { - read_only_property_ids.push_back(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value))); + read_only_property_ids.push_back(nc::details::parse_nc_property_id(nmos::fields::nc::id(property_value))); } } @@ -120,7 +120,7 @@ namespace nmos const auto& allowed_property_values = boost::copy_range>(filtered_property_values | boost::adaptors::filtered([&property_restore_notices, get_control_protocol_class_descriptor, class_id, allow_list_read_only_property_ids](const web::json::value& property_value) { - const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_id = nc::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); // if it's read only and in the allow list then add it if (!nmos::fields::nc::is_read_only(property_descriptor)) @@ -130,13 +130,13 @@ namespace nmos for (const auto& allowed_property_id: allow_list_read_only_property_ids) { - if (nmos::fields::nc::id(property_value) == nmos::details::make_nc_property_id(allowed_property_id)) + if (nmos::fields::nc::id(property_value) == nc::details::make_nc_property_id(allowed_property_id)) { return true; } } // Create a warning notice for any read only property not allowed by the allow list - const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::warning, U("This read only property can not be modified.")); + const auto& property_restore_notice = nc::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::warning, U("This read only property can not be modified.")); web::json::push_back(property_restore_notices, property_restore_notice); return false; @@ -154,7 +154,7 @@ namespace nmos } for (const auto& property_value : property_modify_list) { - const auto& property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_id = nc::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); // hmmm, ideally we would pass the value into modify_resource with the validate @@ -171,7 +171,7 @@ namespace nmos { resource_.data[nmos::fields::nc::name(property_descriptor)] = value; - }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), {{property_id, nmos::nc_property_change_type::type::value_changed, value}})); + }, nc::make_property_changed_event(nmos::fields::nc::oid(resource.data), {{property_id, nmos::nc_property_change_type::type::value_changed, value}})); } } catch(const nmos::control_protocol_exception& e) @@ -179,7 +179,7 @@ namespace nmos // Generate notice for this property utility::stringstream_t ss; ss << U("property error: ") << e.what(); - const auto& property_restore_notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::error, ss.str()); + const auto& property_restore_notice = nc::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::error, ss.str()); web::json::push_back(property_restore_notices, property_restore_notice); } } @@ -244,7 +244,7 @@ namespace nmos if (!remove_device_model_object(*found, child_role_path_array, validate)) { // error in user code - web::json::push_back(block_notices, nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Application error."))); + web::json::push_back(block_notices, nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Application error."))); continue; } @@ -258,14 +258,14 @@ namespace nmos else { // unable to delete resource so report the error and don't update block - web::json::push_back(block_notices, nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Unable to delete resource in Device Model."))); + web::json::push_back(block_notices, nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Unable to delete resource in Device Model."))); } } } else { // unable to delete resource so report the error and don't update block - web::json::push_back(block_notices, nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Unable to find resource in Device Model."))); + web::json::push_back(block_notices, nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Unable to find resource in Device Model."))); } } } @@ -312,7 +312,7 @@ namespace nmos if (oid_property_holder != web::json::value::null() && oid != nmos::fields::nc::value(oid_property_holder).as_integer()) { - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("OID value in block member inconsistent with value in property holder. Property holder value takes precidence.")); + const auto notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("OID value in block member inconsistent with value in property holder. Property holder value takes precidence.")); web::json::push_back(block_notices, notice); } @@ -330,7 +330,7 @@ namespace nmos auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::device_error, status_message); web::json::push_back(object_properties_set_validations, object_properties_set_validation); // also create error notice for the block - const auto block_notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, status_message); + const auto block_notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, status_message); web::json::push_back(block_notices, block_notice); // erase object from object_properties_holder_map so it isn't processed subsequently object_properties_holder_map.erase(child_role_path); @@ -346,7 +346,7 @@ namespace nmos max_oid = std::max(max_oid, nmos::fields::nc::oid(r.data)); } oid = ++max_oid; - const auto block_notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Dynamically generating new OID for new block member.")); + const auto block_notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Dynamically generating new OID for new block member.")); web::json::push_back(block_notices, block_notice); } @@ -359,14 +359,14 @@ namespace nmos { utility::stringstream_t ss; ss << U("Role value in block member inconsistent with value in property holder. Property holder value takes precidence for role=") << role; - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); + const auto notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); web::json::push_back(block_notices, notice); } if (owner != block_oid) { utility::stringstream_t ss; ss << U("Owner value in block member inconsistent with oid of Block. Block oid takes precidence for role=") << role; - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); + const auto notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); web::json::push_back(block_notices, notice); } const auto& owner_property_holder = nmos::get_property_holder(child_object_properties_holder->second, nmos::nc_object_owner_property_id); @@ -374,7 +374,7 @@ namespace nmos { utility::stringstream_t ss; ss << U("Owner value in block property holder inconsistent with oid of Block. Block oid takes precidence for role=") << role; - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("owner"), nmos::nc_property_restore_notice_type::warning, ss.str()); + const auto notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("owner"), nmos::nc_property_restore_notice_type::warning, ss.str()); web::json::push_back(added_object_notices, notice); } const auto& constant_oid_property_holder = nmos::get_property_holder(child_object_properties_holder->second, nmos::nc_object_constant_oid_property_id); @@ -384,7 +384,7 @@ namespace nmos { utility::stringstream_t ss; ss << U("Constant OID value in block property holder inconsistent with oid of Block. Block oid takes precidence for role=") << role; - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); + const auto notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); web::json::push_back(block_notices, notice); } @@ -394,12 +394,12 @@ namespace nmos { utility::stringstream_t ss; ss << U("Class ID property value holder missing for role=") << role; - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("class_id"), nmos::nc_property_restore_notice_type::error, ss.str()); + const auto notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("class_id"), nmos::nc_property_restore_notice_type::error, ss.str()); web::json::push_back(added_object_notices, notice); auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::failed, added_object_notices.as_array(), ss.str()); web::json::push_back(object_properties_set_validations, object_properties_set_validation); // also create error notice for the block - const auto block_notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, ss.str()); + const auto block_notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, ss.str()); web::json::push_back(block_notices, block_notice); // erase object from object_properties_holder_map so it isn't processed subsequently @@ -425,12 +425,12 @@ namespace nmos { utility::stringstream_t ss; ss << U("Device model error: attempting to add unexpected class for role=") << role; - const auto notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("class_id"), nmos::nc_property_restore_notice_type::error, ss.str()); + const auto notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("class_id"), nmos::nc_property_restore_notice_type::error, ss.str()); web::json::push_back(added_object_notices, notice); auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::failed, added_object_notices.as_array(), ss.str()); web::json::push_back(object_properties_set_validations, object_properties_set_validation); // also create error notice for the block - const auto block_notice = nmos::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, ss.str()); + const auto block_notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, ss.str()); web::json::push_back(block_notices, block_notice); // erase object from object_properties_holder_map so it isn't processed subsequently @@ -450,9 +450,9 @@ namespace nmos for (const auto& property_holder: nmos::fields::nc::values(child_object_properties_holder->second)) { - property_values.insert(std::pair(nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)), nmos::fields::nc::value(property_holder))); + property_values.insert(std::pair(nc::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)), nmos::fields::nc::value(property_holder))); } - auto parsed_class_id = nmos::details::parse_nc_class_id(class_id.as_array()); + auto parsed_class_id = nc::details::parse_nc_class_id(class_id.as_array()); auto device_model_object = create_device_model_object(parsed_class_id, oid, constant_oid, owner, role, user_label, touchpoints, validate, property_values); @@ -460,7 +460,7 @@ namespace nmos { for (const auto& property_holder : nmos::fields::nc::values(child_object_properties_holder->second)) { - const auto property_id = nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)); + const auto property_id = nc::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)); const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, parsed_class_id, get_control_protocol_class_descriptor); if (device_model_object.data.has_field(nmos::fields::nc::name(property_descriptor))) @@ -471,14 +471,14 @@ namespace nmos if (object_value != property_holder_value) { // warn - const auto notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::warning, U("Property not updated.")); + const auto notice = nc::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::warning, U("Property not updated.")); web::json::push_back(added_object_notices, notice); } } else { // error doesn't have this property - const auto notice = nmos::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::warning, U("Property not member of created object.")); + const auto notice = nc::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::warning, U("Property not member of created object.")); web::json::push_back(added_object_notices, notice); } } @@ -488,7 +488,7 @@ namespace nmos // Add object to device model nmos::nc::insert_resource(resources, std::move(device_model_object)); - auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(block_member_description, role, oid, constant_oid, nmos::nc_receiver_monitor_class_id, block_member_user_label, owner); + auto block_member_descriptor = nc::details::make_nc_block_member_descriptor(block_member_description, role, oid, constant_oid, nmos::nc_receiver_monitor_class_id, block_member_user_label, owner); members_to_add.push_back(block_member_descriptor); } web::json::push_back(object_properties_set_validations, nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::ok, added_object_notices.as_array())); @@ -544,7 +544,7 @@ namespace nmos { resource.data[nmos::fields::nc::members] = modified_members; - }, nmos::make_property_changed_event(nmos::fields::nc::oid(resource.data), { { nmos::nc_block_members_property_id, nmos::nc_property_change_type::type::value_changed, modified_members } })); + }, nc::make_property_changed_event(nmos::fields::nc::oid(resource.data), { { nmos::nc_block_members_property_id, nmos::nc_property_change_type::type::value_changed, modified_members } })); } return object_properties_set_validations; @@ -586,7 +586,7 @@ namespace nmos const auto& filtered_property_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) | boost::adaptors::filtered([&property_id](const web::json::value& property_holder) { - return property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)); + return property_id == nc::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)); }) ); // There should only be a single property holder for the members @@ -620,7 +620,7 @@ namespace nmos bool is_block_modified(const nmos::resource& resource, const web::json::value& object_properties_holder) { // Are they blocks? - nmos::nc_class_id class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + nmos::nc_class_id class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); if (!nmos::nc::is_block(class_id)) { return false; @@ -628,7 +628,7 @@ namespace nmos const auto& block_members_properties_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) | boost::adaptors::filtered([](const web::json::value& property_holder) { - return nmos::nc_block_members_property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)); + return nmos::nc_block_members_property_id == nc::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)); }) ); // There should only be a single property holder for the members @@ -744,7 +744,7 @@ namespace nmos const auto& object_properties_holder = object_properties_holder_map.at(role_path); - const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(r->data)); + const auto& class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(r->data)); if (nmos::nc::is_block(class_id) && nmos::fields::nc::is_rebuildable(r->data) && restore_mode == nmos::nc_restore_mode::rebuild && is_block_modified(*r, object_properties_holder)) { diff --git a/Development/nmos/control_protocol_behaviour.cpp b/Development/nmos/control_protocol_behaviour.cpp index 49a3483bf..b4f99cd12 100644 --- a/Development/nmos/control_protocol_behaviour.cpp +++ b/Development/nmos/control_protocol_behaviour.cpp @@ -87,7 +87,7 @@ namespace nmos for (const auto& descriptor : descriptors.as_array()) { auto oid = nmos::fields::nc::oid(descriptor); - const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); + const auto& class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); auto status_reporting_delay = nc::get_property(control_protocol_resources, oid, nc_status_monitor_status_reporting_delay, get_control_protocol_class_descriptor, gate); @@ -124,7 +124,7 @@ namespace nmos for (const auto& descriptor : descriptors.as_array()) { const auto& oid = nmos::fields::nc::oid(descriptor); - const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); + const auto& class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); const auto status_reporting_delay = nc::get_property(control_protocol_resources, oid, nc_status_monitor_status_reporting_delay, get_control_protocol_class_descriptor, gate); diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index f93a59752..e33b1706b 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -55,7 +55,7 @@ namespace nmos auto& method_descriptors = control_class_descriptor.method_descriptors; auto found = std::find_if(method_descriptors.begin(), method_descriptors.end(), [&method_id](const experimental::method& method) { - return method_id == details::parse_nc_method_id(nmos::fields::nc::id(std::get<0>(method))); + return method_id == nc::details::parse_nc_method_id(nmos::fields::nc::id(std::get<0>(method))); }); if (method_descriptors.end() != found) { @@ -92,7 +92,7 @@ namespace nmos const bool active = nmos::fields::master_enable(endpoint_active); auto found = nc::find_resource(resources, nmos::types::nc_status_monitor, connection_resource.id); - if (resources.end() != found && nmos::nc::is_status_monitor(details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) + if (resources.end() != found && nmos::nc::is_status_monitor(nc::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) { const auto& oid = nmos::fields::nc::oid(found->data); diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index 088d290fa..c5a041a25 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -23,17 +23,17 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Get property: " << property_id.serialize(); // find the relevant nc_property_descriptor - const auto& property = nc::find_property_descriptor(nmos::details::parse_nc_property_id(property_id), nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { - return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, resource.data.at(nmos::fields::nc::name(property))); + return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, resource.data.at(nmos::fields::nc::name(property))); } // unknown property utility::ostringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do Get"); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } // Set property value @@ -47,8 +47,8 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Set property: " << property_id.serialize() << " value: " << val.serialize(); // find the relevant nc_property_descriptor - const auto property_id_ = nmos::details::parse_nc_property_id(property_id); - const auto& property = nc::find_property_descriptor(property_id_, nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto property_id_ = details::parse_nc_property_id(property_id); + const auto& property = nc::find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (nmos::fields::nc::is_read_only(property)) @@ -56,7 +56,7 @@ namespace nmos utility::ostringstream_t ss; ss << U("can not set read only property: ") << property_id.serialize(); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::read_only}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::read_only}, ss.str()); } if ((val.is_null() && !nmos::fields::nc::is_nullable(property)) @@ -66,18 +66,18 @@ namespace nmos utility::ostringstream_t ss; ss << U("parameter error: cannot set value: ") << val.serialize() << U(" on property: ") << property_id.serialize(); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); } // Special case for BCP-008-01/02 where it specifies that status monitors cannot be disabled if (nmos::fields::nc::name(property).c_str() == nmos::fields::nc::enabled.key - && nc::is_status_monitor(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data))) + && nc::is_status_monitor(details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data))) && !val.as_bool()) { utility::ostringstream_t ss; ss << U("invalid request: cannot disable NcStatusMonitors"); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); } try @@ -98,14 +98,14 @@ namespace nmos }, make_property_changed_event(nmos::fields::nc::oid(resource.data), {{property_id_, nc_property_change_type::type::value_changed, val}})); - return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}); + return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}); } catch (const nmos::control_protocol_exception& e) { utility::ostringstream_t ss; ss << "Set property: " << property_id.serialize() << " value: " << val.serialize() << " error: " << e.what(); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); } } @@ -113,7 +113,7 @@ namespace nmos utility::ostringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << " to do Set"; slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } // Get sequence item @@ -127,7 +127,7 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Get sequence item: " << property_id.serialize() << " index: " << index; // find the relevant nc_property_descriptor - const auto& property = nc::find_property_descriptor(nmos::details::parse_nc_property_id(property_id), nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { const auto& data = resource.data.at(nmos::fields::nc::name(property)); @@ -137,26 +137,26 @@ namespace nmos // property is not a sequence utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceItem"); - return nmos::details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); } if (data.as_array().size() > (size_t)index) { - return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, data.at(index)); + return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, data.at(index)); } // out of bound utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do GetSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::index_out_of_bounds}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::index_out_of_bounds}, ss.str()); } // unknown property utility::ostringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do GetSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } // Set sequence item @@ -171,13 +171,13 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize(); // find the relevant nc_property_descriptor - const auto property_id_ = nmos::details::parse_nc_property_id(property_id); - const auto& property = nc::find_property_descriptor(property_id_, nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto property_id_ = details::parse_nc_property_id(property_id); + const auto& property = nc::find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (nmos::fields::nc::is_read_only(property)) { - return nmos::details::make_nc_method_result({nc_method_status::read_only}); + return details::make_nc_method_result({nc_method_status::read_only}); } auto& data = resource.data.at(nmos::fields::nc::name(property)); @@ -188,7 +188,7 @@ namespace nmos utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do SetSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); } if (data.as_array().size() > (size_t)index) @@ -211,14 +211,14 @@ namespace nmos }, make_property_changed_event(nmos::fields::nc::oid(resource.data), {{property_id_, nc_property_change_type::type::sequence_item_changed, val, nc_id(index)}})); - return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}); + return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}); } catch (const nmos::control_protocol_exception& e) { utility::ostringstream_t ss; ss << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize() << " error: " << e.what(); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); } } @@ -226,14 +226,14 @@ namespace nmos utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do SetSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::index_out_of_bounds}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::index_out_of_bounds}, ss.str()); } // unknown property utility::ostringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do SetSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } // Add item to sequence @@ -249,13 +249,13 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize(); // find the relevant nc_property_descriptor - const auto property_id_ = nmos::details::parse_nc_property_id(property_id); - const auto& property = nc::find_property_descriptor(property_id_, nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto property_id_ = details::parse_nc_property_id(property_id); + const auto& property = nc::find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (nmos::fields::nc::is_read_only(property)) { - return nmos::details::make_nc_method_result({nc_method_status::read_only}); + return details::make_nc_method_result({nc_method_status::read_only}); } if (!nmos::fields::nc::is_sequence(property)) @@ -263,7 +263,7 @@ namespace nmos // property is not a sequence utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do AddSequenceItem"); - return nmos::details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); } auto& data = resource.data.at(nmos::fields::nc::name(property)); @@ -290,14 +290,14 @@ namespace nmos }, make_property_changed_event(nmos::fields::nc::oid(resource.data), {{property_id_, nc_property_change_type::type::sequence_item_added, val, sequence_item_index}})); - return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, sequence_item_index); + return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, sequence_item_index); } catch (const nmos::control_protocol_exception& e) { utility::ostringstream_t ss; ss << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize() << " error: " << e.what(); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); } } @@ -305,7 +305,7 @@ namespace nmos utility::ostringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do AddSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } // Delete sequence item @@ -319,12 +319,12 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Remove sequence item: " << property_id.serialize() << " index: " << index; // find the relevant nc_property_descriptor - const auto& property = nc::find_property_descriptor(nmos::details::parse_nc_property_id(property_id), nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (nmos::fields::nc::is_read_only(property)) { - return nmos::details::make_nc_method_result({nc_method_status::read_only}); + return details::make_nc_method_result({nc_method_status::read_only}); } const auto& data = resource.data.at(nmos::fields::nc::name(property)); @@ -335,7 +335,7 @@ namespace nmos utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do RemoveSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); } if (data.as_array().size() > (size_t)index) @@ -351,23 +351,23 @@ namespace nmos property_changed(resource, nmos::fields::nc::name(property), -2); } - }, make_property_changed_event(nmos::fields::nc::oid(resource.data), {{nmos::details::parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_removed, nc_id(index)}})); + }, make_property_changed_event(nmos::fields::nc::oid(resource.data), {{details::parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_removed, nc_id(index)}})); - return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}); + return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}); } // out of bound utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do RemoveSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::index_out_of_bounds}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::index_out_of_bounds}, ss.str()); } // unknown property utility::ostringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do RemoveSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } // Get sequence length @@ -382,7 +382,7 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Get sequence length: " << property_id.serialize(); // find the relevant nc_property_descriptor - const auto& property = nc::find_property_descriptor(nmos::details::parse_nc_property_id(property_id), nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (!nmos::fields::nc::is_sequence(property)) @@ -391,7 +391,7 @@ namespace nmos utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceLength"); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); } const auto& data = resource.data.at(nmos::fields::nc::name(property)); @@ -402,7 +402,7 @@ namespace nmos if (data.is_null()) { // null - return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, value::null()); + return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, value::null()); } } else @@ -414,17 +414,17 @@ namespace nmos utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << " is a null sequence to do GetSequenceLength"; slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); } } - return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, value(uint32_t(data.as_array().size()))); + return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, value(uint32_t(data.as_array().size()))); } // unknown property utility::ostringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << " to do GetSequenceLength"; slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } // NcBlock methods implementation @@ -442,7 +442,7 @@ namespace nmos auto descriptors = value::array(); nmos::nc::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); - return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); + return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); } // Finds member(s) by path @@ -460,7 +460,7 @@ namespace nmos if (0 == path.size()) { // empty path - return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty path to do FindMembersByPath")); + return details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty path to do FindMembersByPath")); } auto descriptors = value::array(); @@ -492,7 +492,7 @@ namespace nmos utility::ostringstream_t ss; ss << U("role: ") << role.as_string() << U(" not found to do FindMembersByPath"); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); } } else @@ -501,12 +501,12 @@ namespace nmos utility::ostringstream_t ss; ss << U("role: ") << role.as_string() << U(" has no members to do FindMembersByPath"); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); } } web::json::push_back(descriptors, descriptor); - return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); + return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); } // Finds members with given role name or fragment @@ -526,13 +526,13 @@ namespace nmos if (role.empty()) { // empty role - return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty role to do FindMembersByRole")); + return details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty role to do FindMembersByRole")); } auto descriptors = value::array(); nmos::nc::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); - return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); + return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); } // Finds members with given class id @@ -542,16 +542,16 @@ namespace nmos using web::json::value; - const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for + const auto class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for const auto& include_derived = nmos::fields::nc::include_derived(arguments); // If TRUE it will also include derived class descriptors const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks - slog::log(gate, SLOG_FLF) << "Find members with given class id: " << "class_id: " << nmos::details::make_nc_class_id(class_id).serialize(); + slog::log(gate, SLOG_FLF) << "Find members with given class id: " << "class_id: " << details::make_nc_class_id(class_id).serialize(); if (class_id.empty()) { // empty class_id - return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty classId to do FindMembersByClassId")); + return details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty classId to do FindMembersByClassId")); } // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -559,7 +559,7 @@ namespace nmos auto descriptors = value::array(); nmos::nc::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); - return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); + return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); } // NcClassManager methods implementation @@ -568,15 +568,15 @@ namespace nmos { using web::json::value; - const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for + const auto class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements - slog::log(gate, SLOG_FLF) << "Get a single class descriptor: " << "class_id: " << nmos::details::make_nc_class_id(class_id).serialize(); + slog::log(gate, SLOG_FLF) << "Get a single class descriptor: " << "class_id: " << details::make_nc_class_id(class_id).serialize(); if (class_id.empty()) { // empty class_id - return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty classId to do GetControlClass")); + return details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty classId to do GetControlClass")); } // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -609,13 +609,13 @@ namespace nmos } } const auto descriptor = fixed_role.is_null() - ? nmos::details::make_nc_class_descriptor(description, class_id, name, property_descriptors, method_descriptors, event_descriptors) - : nmos::details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), property_descriptors, method_descriptors, event_descriptors); + ? details::make_nc_class_descriptor(description, class_id, name, property_descriptors, method_descriptors, event_descriptors) + : details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), property_descriptors, method_descriptors, event_descriptors); - return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptor); + return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptor); } - return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, U("classId not found")); + return details::make_nc_method_result_error({nc_method_status::parameter_error}, U("classId not found")); } // Get a single datatype descriptor @@ -631,7 +631,7 @@ namespace nmos if (name.empty()) { // empty name - return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty name to do GetDatatype")); + return details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty name to do GetDatatype")); } const auto& datatype = get_control_protocol_datatype_descriptor(name); @@ -671,10 +671,10 @@ namespace nmos } } - return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptor); + return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptor); } - return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, U("name not found")); + return details::make_nc_method_result_error({nc_method_status::parameter_error}, U("name not found")); } // NcReceiverMonitor methods implementation @@ -697,10 +697,10 @@ namespace nmos }); })); - return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, nc_counter_sequence); + return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, nc_counter_sequence); } - return nmos::details::make_nc_method_result_error({nmos::nc_method_status::method_not_implemented}, U("not implemented")); + return details::make_nc_method_result_error({nmos::nc_method_status::method_not_implemented}, U("not implemented")); } } @@ -749,14 +749,14 @@ namespace nmos std::pair(nc_status_monitor_overall_status_message_property_id, web::json::value::null()), }; - const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); // reset all counters const std::vector> property_values = nmos::nc::is_sender_monitor(class_id) ? sender_property_values : receiver_property_values; for (const auto& property_value : property_values) { - const auto& property = nc::find_property_descriptor(property_value.first, nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(property_value.first, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { try @@ -777,9 +777,9 @@ namespace nmos catch (const nmos::control_protocol_exception& e) { utility::ostringstream_t ss; - ss << "Reset counters: " << nmos::details::make_nc_property_id(property_value.first).serialize() << " error: " << e.what(); + ss << "Reset counters: " << details::make_nc_property_id(property_value.first).serialize() << " error: " << e.what(); slog::log(gate, SLOG_FLF) << ss.str(); - return nmos::details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + return details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); } } } @@ -789,7 +789,7 @@ namespace nmos reset_monitor(); } - return nmos::details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}); + return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}); } } } diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 7344b448b..f00d67a07 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -6,2535 +6,2538 @@ namespace nmos { - namespace details + namespace nc { - web::json::value make_nc_method_result(const nc_method_result& method_result) + namespace details { - using web::json::value_of; + web::json::value make_nc_method_result(const nc_method_result& method_result) + { + using web::json::value_of; - return value_of({ - { nmos::fields::nc::status, method_result.status } - }); - } + return value_of({ + { nmos::fields::nc::status, method_result.status } + }); + } - web::json::value make_nc_method_result_error(const nc_method_result& method_result, const utility::string_t& error_message) - { - auto result = make_nc_method_result(method_result); - if (!error_message.empty()) { result[nmos::fields::nc::error_message] = web::json::value::string(error_message); } - return result; - } + web::json::value make_nc_method_result_error(const nc_method_result& method_result, const utility::string_t& error_message) + { + auto result = make_nc_method_result(method_result); + if (!error_message.empty()) { result[nmos::fields::nc::error_message] = web::json::value::string(error_message); } + return result; + } - web::json::value make_nc_method_result(const nc_method_result& method_result, const web::json::value& value) - { - auto result = make_nc_method_result(method_result); - result[nmos::fields::nc::value] = value; - return result; - } + web::json::value make_nc_method_result(const nc_method_result& method_result, const web::json::value& value) + { + auto result = make_nc_method_result(method_result); + result[nmos::fields::nc::value] = value; + return result; + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncelementid - web::json::value make_nc_element_id(uint16_t level, uint16_t index) - { - using web::json::value_of; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncelementid + web::json::value make_nc_element_id(uint16_t level, uint16_t index) + { + using web::json::value_of; - return value_of({ - { nmos::fields::nc::level, level }, - { nmos::fields::nc::index, index } - }); - } - web::json::value make_nc_element_id(const nc_element_id& id) - { - return make_nc_element_id(id.level, id.index); - } - nc_element_id parse_nc_element_id(const web::json::value& id) - { - return { uint16_t(nmos::fields::nc::level(id)), uint16_t(nmos::fields::nc::index(id)) }; - } + return value_of({ + { nmos::fields::nc::level, level }, + { nmos::fields::nc::index, index } + }); + } + web::json::value make_nc_element_id(const nc_element_id& id) + { + return make_nc_element_id(id.level, id.index); + } + nc_element_id parse_nc_element_id(const web::json::value& id) + { + return { uint16_t(nmos::fields::nc::level(id)), uint16_t(nmos::fields::nc::index(id)) }; + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventid - web::json::value make_nc_event_id(const nc_event_id& id) - { - return make_nc_element_id(id); - } - nc_event_id parse_nc_event_id(const web::json::value& id) - { - return parse_nc_element_id(id); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventid + web::json::value make_nc_event_id(const nc_event_id& id) + { + return make_nc_element_id(id); + } + nc_event_id parse_nc_event_id(const web::json::value& id) + { + return parse_nc_element_id(id); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethodid - web::json::value make_nc_method_id(const nc_method_id& id) - { - return make_nc_element_id(id); - } - nc_method_id parse_nc_method_id(const web::json::value& id) - { - return parse_nc_element_id(id); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethodid + web::json::value make_nc_method_id(const nc_method_id& id) + { + return make_nc_element_id(id); + } + nc_method_id parse_nc_method_id(const web::json::value& id) + { + return parse_nc_element_id(id); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyid - web::json::value make_nc_property_id(const nc_property_id& id) - { - return make_nc_element_id(id); - } - nc_property_id parse_nc_property_id(const web::json::value& id) - { - return parse_nc_element_id(id); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyid + web::json::value make_nc_property_id(const nc_property_id& id) + { + return make_nc_element_id(id); + } + nc_property_id parse_nc_property_id(const web::json::value& id) + { + return parse_nc_element_id(id); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassid - web::json::value make_nc_class_id(const nc_class_id& class_id) - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassid + web::json::value make_nc_class_id(const nc_class_id& class_id) + { + using web::json::value; - auto nc_class_id = value::array(); - for (const auto class_id_item : class_id) { web::json::push_back(nc_class_id, class_id_item); } - return nc_class_id; - } - nc_class_id parse_nc_class_id(const web::json::array& class_id_) - { - nc_class_id class_id; - for (auto& element : class_id_) + auto nc_class_id = value::array(); + for (const auto class_id_item : class_id) { web::json::push_back(nc_class_id, class_id_item); } + return nc_class_id; + } + nc_class_id parse_nc_class_id(const web::json::array& class_id_) { - class_id.push_back(element.as_integer()); + nc_class_id class_id; + for (auto& element : class_id_) + { + class_id.push_back(element.as_integer()); + } + return class_id; } - return class_id; - } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanufacturer - web::json::value make_nc_manufacturer(const utility::string_t& name, const web::json::value& organization_id, const web::json::value& website) - { - using web::json::value_of; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanufacturer + web::json::value make_nc_manufacturer(const utility::string_t& name, const web::json::value& organization_id, const web::json::value& website) + { + using web::json::value_of; - return value_of({ - { nmos::fields::nc::name, name }, - { nmos::fields::nc::organization_id, organization_id }, - { nmos::fields::nc::website, website } - }); - } - web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id, const web::uri& website) - { - using web::json::value; + return value_of({ + { nmos::fields::nc::name, name }, + { nmos::fields::nc::organization_id, organization_id }, + { nmos::fields::nc::website, website } + }); + } + web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id, const web::uri& website) + { + using web::json::value; - return make_nc_manufacturer(name, organization_id, value::string(website.to_string())); - } - web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id) - { - using web::json::value; + return make_nc_manufacturer(name, organization_id, value::string(website.to_string())); + } + web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id) + { + using web::json::value; - return make_nc_manufacturer(name, organization_id, value::null()); - } - web::json::value make_nc_manufacturer(const utility::string_t& name) - { - using web::json::value; + return make_nc_manufacturer(name, organization_id, value::null()); + } + web::json::value make_nc_manufacturer(const utility::string_t& name) + { + using web::json::value; - return make_nc_manufacturer(name, value::null(), value::null()); - } + return make_nc_manufacturer(name, value::null(), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncproduct - // brand_name can be null - // uuid can be null - // description can be null - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, - const web::json::value& brand_name, const web::json::value& uuid, const web::json::value& description) - { - using web::json::value_of; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncproduct + // brand_name can be null + // uuid can be null + // description can be null + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const web::json::value& brand_name, const web::json::value& uuid, const web::json::value& description) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::name, name }, + { nmos::fields::nc::key, key }, + { nmos::fields::nc::revision_level, revision_level }, + { nmos::fields::nc::brand_name, brand_name }, + { nmos::fields::nc::uuid, uuid }, + { nmos::fields::nc::description, description } + }); + } + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const utility::string_t& brand_name, const nc_uuid& uuid, const utility::string_t& description) + { + using web::json::value; - return value_of({ - { nmos::fields::nc::name, name }, - { nmos::fields::nc::key, key }, - { nmos::fields::nc::revision_level, revision_level }, - { nmos::fields::nc::brand_name, brand_name }, - { nmos::fields::nc::uuid, uuid }, - { nmos::fields::nc::description, description } - }); - } - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, - const utility::string_t& brand_name, const nc_uuid& uuid, const utility::string_t& description) - { - using web::json::value; + return make_nc_product(name, key, revision_level, value::string(brand_name), value::string(uuid), value::string(description)); + } + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const utility::string_t& brand_name, const nc_uuid& uuid) + { + using web::json::value; - return make_nc_product(name, key, revision_level, value::string(brand_name), value::string(uuid), value::string(description)); - } - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, - const utility::string_t& brand_name, const nc_uuid& uuid) - { - using web::json::value; + return make_nc_product(name, key, revision_level, value::string(brand_name), value::string(uuid), value::null()); + } + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const utility::string_t& brand_name) + { + using web::json::value; - return make_nc_product(name, key, revision_level, value::string(brand_name), value::string(uuid), value::null()); - } - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, - const utility::string_t& brand_name) - { - using web::json::value; + return make_nc_product(name, key, revision_level, value::string(brand_name), value::null(), value::null()); + } + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level) + { + using web::json::value; - return make_nc_product(name, key, revision_level, value::string(brand_name), value::null(), value::null()); - } - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level) - { - using web::json::value; + return make_nc_product(name, key, revision_level, value::null(), value::null(), value::null()); + } - return make_nc_product(name, key, revision_level, value::null(), value::null(), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdeviceoperationalstate + // device_specific_details can be null + web::json::value make_nc_device_operational_state(nc_device_generic_state::state generic_state, const web::json::value& device_specific_details) + { + using web::json::value_of; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdeviceoperationalstate - // device_specific_details can be null - web::json::value make_nc_device_operational_state(nc_device_generic_state::state generic_state, const web::json::value& device_specific_details) - { - using web::json::value_of; + return value_of({ + { nmos::fields::nc::generic_state, generic_state }, + { nmos::fields::nc::device_specific_details, device_specific_details } + }); + } - return value_of({ - { nmos::fields::nc::generic_state, generic_state }, - { nmos::fields::nc::device_specific_details, device_specific_details } - }); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdescriptor + // description can be null + web::json::value make_nc_descriptor(const web::json::value& description) + { + using web::json::value_of; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdescriptor - // description can be null - web::json::value make_nc_descriptor(const web::json::value& description) - { - using web::json::value_of; + return value_of({ { nmos::fields::nc::description, description } }); + } - return value_of({ { nmos::fields::nc::description, description } }); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblockmemberdescriptor + // description can be null + // user_label can be null + web::json::value make_nc_block_member_descriptor(const web::json::value& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const web::json::value& user_label, nc_oid owner) + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblockmemberdescriptor - // description can be null - // user_label can be null - web::json::value make_nc_block_member_descriptor(const web::json::value& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const web::json::value& user_label, nc_oid owner) - { - using web::json::value; + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::role] = value::string(role); + data[nmos::fields::nc::oid] = oid; + data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); + data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); + data[nmos::fields::nc::user_label] = user_label; + data[nmos::fields::nc::owner] = owner; - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::role] = value::string(role); - data[nmos::fields::nc::oid] = oid; - data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); - data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); - data[nmos::fields::nc::user_label] = user_label; - data[nmos::fields::nc::owner] = owner; + return data; + } + web::json::value make_nc_block_member_descriptor(const utility::string_t& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const utility::string_t& user_label, nc_oid owner) + { + using web::json::value; - return data; - } - web::json::value make_nc_block_member_descriptor(const utility::string_t& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const utility::string_t& user_label, nc_oid owner) - { - using web::json::value; + return make_nc_block_member_descriptor(value::string(description), role, oid, constant_oid, class_id, value::string(user_label), owner); + } - return make_nc_block_member_descriptor(value::string(description), role, oid, constant_oid, class_id, value::string(user_label), owner); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassdescriptor + // description can be null + // fixedRole can be null + web::json::value make_nc_class_descriptor(const web::json::value& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassdescriptor - // description can be null - // fixedRole can be null - web::json::value make_nc_class_descriptor(const web::json::value& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) - { - using web::json::value; + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::fixed_role] = fixed_role; + data[nmos::fields::nc::properties] = properties; + data[nmos::fields::nc::methods] = methods; + data[nmos::fields::nc::events] = events; - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); - data[nmos::fields::nc::name] = value::string(name); - data[nmos::fields::nc::fixed_role] = fixed_role; - data[nmos::fields::nc::properties] = properties; - data[nmos::fields::nc::methods] = methods; - data[nmos::fields::nc::events] = events; + return data; + } + web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) + { + using web::json::value; - return data; - } - web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) - { - using web::json::value; + return make_nc_class_descriptor(value::string(description), class_id, name, value::string(fixed_role), properties, methods, events); + } + web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) + { + using web::json::value; - return make_nc_class_descriptor(value::string(description), class_id, name, value::string(fixed_role), properties, methods, events); - } - web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) - { - using web::json::value; + return make_nc_class_descriptor(value::string(description), class_id, name, value::null(), properties, methods, events); + } - return make_nc_class_descriptor(value::string(description), class_id, name, value::null(), properties, methods, events); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncenumitemdescriptor + // description can be null + web::json::value make_nc_enum_item_descriptor(const web::json::value& description, const nc_name& name, uint16_t val) + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncenumitemdescriptor - // description can be null - web::json::value make_nc_enum_item_descriptor(const web::json::value& description, const nc_name& name, uint16_t val) - { - using web::json::value; + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::value] = val; - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::name] = value::string(name); - data[nmos::fields::nc::value] = val; + return data; + } + web::json::value make_nc_enum_item_descriptor(const utility::string_t& description, const nc_name& name, uint16_t val) + { + using web::json::value; - return data; - } - web::json::value make_nc_enum_item_descriptor(const utility::string_t& description, const nc_name& name, uint16_t val) - { - using web::json::value; + return make_nc_enum_item_descriptor(value::string(description), name, val); + } - return make_nc_enum_item_descriptor(value::string(description), name, val); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventdescriptor + // description can be null + // id = make_nc_event_id(level, index) + web::json::value make_nc_event_descriptor(const web::json::value& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated) + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventdescriptor - // description can be null - // id = make_nc_event_id(level, index) - web::json::value make_nc_event_descriptor(const web::json::value& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated) - { - using web::json::value; + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::id] = make_nc_event_id(id); + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::event_datatype] = value::string(event_datatype); + data[nmos::fields::nc::is_deprecated] = value::boolean(is_deprecated); - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::id] = make_nc_event_id(id); - data[nmos::fields::nc::name] = value::string(name); - data[nmos::fields::nc::event_datatype] = value::string(event_datatype); - data[nmos::fields::nc::is_deprecated] = value::boolean(is_deprecated); + return data; + } + web::json::value make_nc_event_descriptor(const utility::string_t& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated) + { + using web::json::value; - return data; - } - web::json::value make_nc_event_descriptor(const utility::string_t& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated) - { - using web::json::value; + return make_nc_event_descriptor(value::string(description), id, name, event_datatype, is_deprecated); + } - return make_nc_event_descriptor(value::string(description), id, name, event_datatype, is_deprecated); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncfielddescriptor + // description can be null + // type_name can be null + // constraints can be null + web::json::value make_nc_field_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncfielddescriptor - // description can be null - // type_name can be null - // constraints can be null - web::json::value make_nc_field_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) - { - using web::json::value; + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::type_name] = type_name; + data[nmos::fields::nc::is_nullable] = value::boolean(is_nullable); + data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); + data[nmos::fields::nc::constraints] = constraints; - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::name] = value::string(name); - data[nmos::fields::nc::type_name] = type_name; - data[nmos::fields::nc::is_nullable] = value::boolean(is_nullable); - data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); - data[nmos::fields::nc::constraints] = constraints; + return data; + } + web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + { + using web::json::value; - return data; - } - web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) - { - using web::json::value; + return make_nc_field_descriptor(value::string(description), name, value::string(type_name), is_nullable, is_sequence, constraints); + } + web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + { + using web::json::value; - return make_nc_field_descriptor(value::string(description), name, value::string(type_name), is_nullable, is_sequence, constraints); - } - web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints) - { - using web::json::value; + return make_nc_field_descriptor(value::string(description), name, value::null(), is_nullable, is_sequence, constraints); + } - return make_nc_field_descriptor(value::string(description), name, value::null(), is_nullable, is_sequence, constraints); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethoddescriptor + // description can be null + // id = make_nc_method_id(level, index) + // sequence parameters + web::json::value make_nc_method_descriptor(const web::json::value& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethoddescriptor - // description can be null - // id = make_nc_method_id(level, index) - // sequence parameters - web::json::value make_nc_method_descriptor(const web::json::value& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) - { - using web::json::value; + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::id] = make_nc_method_id(id); + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::result_datatype] = value::string(result_datatype); + data[nmos::fields::nc::parameters] = parameters; + data[nmos::fields::nc::is_deprecated] = value::boolean(is_deprecated); - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::id] = make_nc_method_id(id); - data[nmos::fields::nc::name] = value::string(name); - data[nmos::fields::nc::result_datatype] = value::string(result_datatype); - data[nmos::fields::nc::parameters] = parameters; - data[nmos::fields::nc::is_deprecated] = value::boolean(is_deprecated); + return data; + } + web::json::value make_nc_method_descriptor(const utility::string_t& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) + { + using web::json::value; - return data; - } - web::json::value make_nc_method_descriptor(const utility::string_t& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) - { - using web::json::value; + return make_nc_method_descriptor(value::string(description), id, name, result_datatype, parameters, is_deprecated); + } - return make_nc_method_descriptor(value::string(description), id, name, result_datatype, parameters, is_deprecated); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterdescriptor + // description can be null + // type_name can be null + web::json::value make_nc_parameter_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterdescriptor - // description can be null - // type_name can be null - web::json::value make_nc_parameter_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) - { - using web::json::value; + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::type_name] = type_name; + data[nmos::fields::nc::is_nullable] = value::boolean(is_nullable); + data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); + data[nmos::fields::nc::constraints] = constraints; - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::name] = value::string(name); - data[nmos::fields::nc::type_name] = type_name; - data[nmos::fields::nc::is_nullable] = value::boolean(is_nullable); - data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); - data[nmos::fields::nc::constraints] = constraints; + return data; + } + web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + { + using web::json::value; - return data; - } - web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints) - { - using web::json::value; + return make_nc_parameter_descriptor(value::string(description), name, value::null(), is_nullable, is_sequence, constraints); + } + web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + { + using web::json::value; - return make_nc_parameter_descriptor(value::string(description), name, value::null(), is_nullable, is_sequence, constraints); - } - web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) - { - using web::json::value; + return make_nc_parameter_descriptor(value::string(description), name, value::string(type_name), is_nullable, is_sequence, constraints); + } - return make_nc_parameter_descriptor(value::string(description), name, value::string(type_name), is_nullable, is_sequence, constraints); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertydescriptor + // description can be null + // constraints can be null + web::json::value make_nc_property_descriptor(const web::json::value& description, const nc_property_id& id, const nc_name& name, const web::json::value& type_name, + bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) + { + using web::json::value; + + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::id] = make_nc_property_id(id); + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::type_name] = type_name; + data[nmos::fields::nc::is_read_only] = value::boolean(is_read_only); + data[nmos::fields::nc::is_nullable] = value::boolean(is_nullable); + data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); + data[nmos::fields::nc::is_deprecated] = value::boolean(is_deprecated); + data[nmos::fields::nc::constraints] = constraints; + + return data; + } + web::json::value make_nc_property_descriptor(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, + bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertydescriptor - // description can be null - // constraints can be null - web::json::value make_nc_property_descriptor(const web::json::value& description, const nc_property_id& id, const nc_name& name, const web::json::value& type_name, - bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) - { - using web::json::value; + return nmos::nc::details::make_nc_property_descriptor(value::string(description), id, name, value::string(type_name), is_read_only, is_nullable, is_sequence, is_deprecated, constraints); + } - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::id] = make_nc_property_id(id); - data[nmos::fields::nc::name] = value::string(name); - data[nmos::fields::nc::type_name] = type_name; - data[nmos::fields::nc::is_read_only] = value::boolean(is_read_only); - data[nmos::fields::nc::is_nullable] = value::boolean(is_nullable); - data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); - data[nmos::fields::nc::is_deprecated] = value::boolean(is_deprecated); - data[nmos::fields::nc::constraints] = constraints; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptor + // description can be null + // constraints can be null + web::json::value make_nc_datatype_descriptor(const web::json::value& description, const nc_name& name, nc_datatype_type::type type, const web::json::value& constraints) + { + using web::json::value; - return data; - } - web::json::value make_nc_property_descriptor(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, - bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) - { - using web::json::value; + auto data = make_nc_descriptor(description); + data[nmos::fields::nc::name] = value::string(name); + data[nmos::fields::nc::type] = type; + data[nmos::fields::nc::constraints] = constraints; - return nmos::details::make_nc_property_descriptor(value::string(description), id, name, value::string(type_name), is_read_only, is_nullable, is_sequence, is_deprecated, constraints); - } + return data; + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptor - // description can be null - // constraints can be null - web::json::value make_nc_datatype_descriptor(const web::json::value& description, const nc_name& name, nc_datatype_type::type type, const web::json::value& constraints) - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorenum + // description can be null + // constraints can be null + // items: sequence + web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints) + { + auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Enum, constraints); + data[nmos::fields::nc::items] = items; - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::name] = value::string(name); - data[nmos::fields::nc::type] = type; - data[nmos::fields::nc::constraints] = constraints; + return data; + } + web::json::value make_nc_datatype_descriptor_enum(const utility::string_t& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints) + { + using web::json::value; - return data; - } + return make_nc_datatype_descriptor_enum(value::string(description), name, items, constraints); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorenum - // description can be null - // constraints can be null - // items: sequence - web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints) - { - auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Enum, constraints); - data[nmos::fields::nc::items] = items; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorprimitive + // description can be null + // constraints can be null + web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const nc_name& name, const web::json::value& constraints) + { + return make_nc_datatype_descriptor(description, name, nc_datatype_type::Primitive, constraints); + } + web::json::value make_nc_datatype_descriptor_primitive(const utility::string_t& description, const nc_name& name, const web::json::value& constraints) + { + using web::json::value; - return data; - } - web::json::value make_nc_datatype_descriptor_enum(const utility::string_t& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints) - { - using web::json::value; + return make_nc_datatype_descriptor_primitive(value::string(description), name, constraints); + } - return make_nc_datatype_descriptor_enum(value::string(description), name, items, constraints); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorstruct + // description can be null + // constraints can be null + // fields: sequence + // parent_type can be null + web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const nc_name& name, const web::json::value& fields, const web::json::value& parent_type, const web::json::value& constraints) + { + auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Struct, constraints); + data[nmos::fields::nc::fields] = fields; + data[nmos::fields::nc::parent_type] = parent_type; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorprimitive - // description can be null - // constraints can be null - web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const nc_name& name, const web::json::value& constraints) - { - return make_nc_datatype_descriptor(description, name, nc_datatype_type::Primitive, constraints); - } - web::json::value make_nc_datatype_descriptor_primitive(const utility::string_t& description, const nc_name& name, const web::json::value& constraints) - { - using web::json::value; + return data; + } + web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const utility::string_t& parent_type, const web::json::value& constraints) + { + using web::json::value; - return make_nc_datatype_descriptor_primitive(value::string(description), name, constraints); - } + return make_nc_datatype_descriptor_struct(value::string(description), name, fields, value::string(parent_type), constraints); + } + web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const web::json::value& constraints) + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorstruct - // description can be null - // constraints can be null - // fields: sequence - // parent_type can be null - web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const nc_name& name, const web::json::value& fields, const web::json::value& parent_type, const web::json::value& constraints) - { - auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Struct, constraints); - data[nmos::fields::nc::fields] = fields; - data[nmos::fields::nc::parent_type] = parent_type; + return make_nc_datatype_descriptor_struct(value::string(description), name, fields, value::null(), constraints); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptortypedef + // description can be null + // constraints can be null + web::json::value make_nc_datatype_typedef(const web::json::value& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints) + { + using web::json::value; + + auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Typedef, constraints); + data[nmos::fields::nc::parent_type] = value::string(parent_type); + data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); + + return data; + } + web::json::value make_nc_datatype_typedef(const utility::string_t& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints) + { + using web::json::value; + + return make_nc_datatype_typedef(value::string(description), name, is_sequence, parent_type, constraints); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraints + web::json::value make_nc_property_constraints(const nc_property_id& property_id, const web::json::value& default_value) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::property_id, make_nc_property_id(property_id) }, + { nmos::fields::nc::default_value, default_value } + }); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsnumber + web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, const web::json::value& default_value, const web::json::value& minimum, const web::json::value& maximum, const web::json::value& step) + { + using web::json::value; + + auto data = make_nc_property_constraints(property_id, default_value); + data[nmos::fields::nc::minimum] = minimum; + data[nmos::fields::nc::maximum] = maximum; + data[nmos::fields::nc::step] = step; + + return data; + } + web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step) + { + using web::json::value; + + return make_nc_property_constraints_number(property_id, value(default_value), value(minimum), value(maximum), value(step)); + } + web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t minimum, uint64_t maximum, uint64_t step) + { + using web::json::value; + + return make_nc_property_constraints_number(property_id, value::null(), minimum, maximum, step); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsstring + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const web::json::value& default_value, const web::json::value& max_characters, const web::json::value& pattern) + { + using web::json::value; + + auto data = make_nc_property_constraints(property_id, default_value); + data[nmos::fields::nc::max_characters] = max_characters; + data[nmos::fields::nc::pattern] = pattern; + + return data; + } + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern) + { + using web::json::value; + + return make_nc_property_constraints_string(property_id, value::string(default_value), max_characters, value::string(pattern)); + } + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters, const nc_regex& pattern) + { + using web::json::value; + + return make_nc_property_constraints_string(property_id, value::null(), max_characters, value::string(pattern)); + } + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters) + { + using web::json::value; + + return make_nc_property_constraints_string(property_id, value::null(), max_characters, value::null()); + } + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const nc_regex& pattern) + { + using web::json::value; + + return make_nc_property_constraints_string(property_id, value::null(), value::null(), value::string(pattern)); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraints + web::json::value make_nc_parameter_constraints(const web::json::value& default_value) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::default_value, default_value } + }); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber + web::json::value make_nc_parameter_constraints_number(const web::json::value& default_value, const web::json::value& minimum, const web::json::value& maximum, const web::json::value& step) + { + using web::json::value; + + auto data = make_nc_parameter_constraints(default_value); + data[nmos::fields::nc::minimum] = minimum; + data[nmos::fields::nc::maximum] = maximum; + data[nmos::fields::nc::step] = step; + + return data; + } + web::json::value make_nc_parameter_constraints_number(uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step) + { + using web::json::value; + + return make_nc_parameter_constraints_number(value(default_value), value(minimum), value(maximum), value(step)); + } + web::json::value make_nc_parameter_constraints_number(uint64_t minimum, uint64_t maximum, uint64_t step) + { + using web::json::value; + + return make_nc_parameter_constraints_number(value::null(), minimum, maximum, step); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring + web::json::value make_nc_parameter_constraints_string(const web::json::value& default_value, const web::json::value& max_characters, const web::json::value& pattern) + { + auto data = make_nc_parameter_constraints(default_value); + data[nmos::fields::nc::max_characters] = max_characters; + data[nmos::fields::nc::pattern] = pattern; + + return data; + } + web::json::value make_nc_parameter_constraints_string(const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern) + { + using web::json::value; + + return make_nc_parameter_constraints_string(value::string(default_value), max_characters, value::string(pattern)); + } + web::json::value make_nc_parameter_constraints_string(uint32_t max_characters, const nc_regex& pattern) + { + using web::json::value; + + return make_nc_parameter_constraints_string(value::null(), max_characters, value::string(pattern)); + } + web::json::value make_nc_parameter_constraints_string(uint32_t max_characters) + { + using web::json::value; + + return make_nc_parameter_constraints_string(value::null(), max_characters, value::null()); + } + web::json::value make_nc_parameter_constraints_string(const nc_regex& pattern) + { + using web::json::value; + + return make_nc_parameter_constraints_string(value::null(), value::null(), value::string(pattern)); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresource + web::json::value make_nc_touchpoint_resource(const nc_touchpoint_resource& resource) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::resource_type, resource.resource_type } + }); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresourcenmos + web::json::value make_nc_touchpoint_resource_nmos(const nc_touchpoint_resource_nmos& resource) + { + using web::json::value; + + auto data = make_nc_touchpoint_resource(resource); + data[nmos::fields::nc::id] = value::string(resource.id); + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresourcenmoschannelmapping + web::json::value make_nc_touchpoint_resource_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource) + { + using web::json::value; + + auto data = make_nc_touchpoint_resource_nmos(resource); + data[nmos::fields::nc::io_id] = value::string(resource.io_id); + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpoint + web::json::value make_nc_touchpoint(const utility::string_t& context_namespace) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::context_namespace, context_namespace } + }); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointnmos + web::json::value make_nc_touchpoint_nmos(const nc_touchpoint_resource_nmos& resource) + { + auto data = make_nc_touchpoint(U("x-nmos")); + data[nmos::fields::nc::resource] = make_nc_touchpoint_resource_nmos(resource); + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointnmoschannelmapping + web::json::value make_nc_touchpoint_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource) + { + auto data = make_nc_touchpoint(U("x-nmos/channelmapping")); + data[nmos::fields::nc::resource] = make_nc_touchpoint_resource_nmos_channel_mapping(resource); - return data; + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject + web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + { + using web::json::value; + + const auto id = utility::conversions::details::to_string_t(oid); + auto data = nmos::details::make_resource_core(id, user_label.is_null() ? U("") : user_label.as_string(), description); // required for nmos::resource + data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); + data[nmos::fields::nc::oid] = oid; + data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); + data[nmos::fields::nc::owner] = owner; + data[nmos::fields::nc::role] = value::string(role); + data[nmos::fields::nc::user_label] = user_label; + data[nmos::fields::nc::touchpoints] = touchpoints; + data[nmos::fields::nc::runtime_property_constraints] = runtime_property_constraints; // level 2 runtime constraints. See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + + // IS-14 metadata fields + // These fields are "invisible" as they are not part of the NcObject definition + // use make_rebuildable function to declare an control protocol resource rebuildable + data[nmos::fields::nc::is_rebuildable] = value::boolean(false); + // use allowed_member_classes to restrict the types of object that an NcBlock can contain + data[nmos::fields::nc::allowed_members_classes] = value::array(); + // use to indicate dependencies of an object in the device model + data[nmos::fields::nc::dependency_paths] = value::array(); + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock + web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members) + { + using web::json::value; + + auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); + data[nmos::fields::nc::enabled] = value::boolean(enabled); + data[nmos::fields::nc::members] = members; + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker + web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled) + { + using web::json::value; + + auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); + data[nmos::fields::nc::enabled] = value::boolean(enabled); + + return data; + } + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstatusmonitor + web::json::value make_nc_status_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, uint64_t status_reporting_delay) + { + using web::json::value; + + auto data = make_nc_worker(class_id, oid, constant_oid, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, enabled); + data[nmos::fields::nc::overall_status] = value::number(overall_status); + data[nmos::fields::nc::overall_status_message] = value::string(overall_status_message); + data[nmos::fields::nc::status_reporting_delay] = value::number(status_reporting_delay); + + return data; + } + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor + web::json::value make_receiver_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, nc_link_status::status link_status, const utility::string_t& link_status_message, nc_connection_status::status connection_status, const utility::string_t& connection_status_message, nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, const web::json::value& synchronization_source_id, nc_stream_status::status stream_status, const utility::string_t& stream_status_message, uint32_t status_reporting_delay, bool auto_reset_monitor) + { + using web::json::value; + + auto data = make_nc_status_monitor(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints, enabled, overall_status, overall_status_message, status_reporting_delay); + + data[nmos::fields::nc::link_status] = value::number(link_status); + data[nmos::fields::nc::link_status_message] = value::string(link_status_message); + data[nmos::fields::nc::link_status_transition_counter] = value::number(0); + data[nmos::fields::nc::connection_status] = value::number(connection_status); + data[nmos::fields::nc::connection_status_message] = value::string(connection_status_message); + data[nmos::fields::nc::connection_status_transition_counter] = value::number(0); + data[nmos::fields::nc::external_synchronization_status] = value::number(external_synchronization_status); + data[nmos::fields::nc::external_synchronization_status_message] = value::string(external_synchronization_status_message); + data[nmos::fields::nc::external_synchronization_status_transition_counter] = value::number(0); + data[nmos::fields::nc::synchronization_source_id] = synchronization_source_id; + data[nmos::fields::nc::stream_status] = value::number(stream_status); + data[nmos::fields::nc::stream_status_message] = value::string(stream_status_message); + data[nmos::fields::nc::stream_status_transition_counter] = value::number(0); + data[nmos::fields::nc::auto_reset_monitor] = value::boolean(auto_reset_monitor); + + // Pending status updates + data[nmos::fields::nc::monitor_activation_time] = value::number(0); + data[nmos::fields::nc::link_status_pending] = value::number(link_status); + data[nmos::fields::nc::link_status_message_pending] = value::string(link_status_message); + data[nmos::fields::nc::link_status_pending_received_time] = value::number(0); + data[nmos::fields::nc::connection_status_pending] = value::number(connection_status); + data[nmos::fields::nc::connection_status_message_pending] = value::string(connection_status_message); + data[nmos::fields::nc::connection_status_pending_received_time] = value::number(0); + data[nmos::fields::nc::external_synchronization_status_pending] = value::number(external_synchronization_status); + data[nmos::fields::nc::external_synchronization_status_message_pending] = value::string(external_synchronization_status_message); + data[nmos::fields::nc::external_synchronization_status_pending_received_time] = value::number(0); + data[nmos::fields::nc::stream_status_pending] = value::number(stream_status); + data[nmos::fields::nc::stream_status_message_pending] = value::string(stream_status_message); + data[nmos::fields::nc::stream_status_pending_received_time] = value::number(0); + + return data; + } + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsendermonitor + web::json::value make_sender_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, nc_link_status::status link_status, const utility::string_t& link_status_message, nc_transmission_status::status transmission_status, const utility::string_t& transmission_status_message, nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, const web::json::value& synchronization_source_id, nc_essence_status::status essence_status, const utility::string_t& essence_status_message, uint32_t status_reporting_delay, bool auto_reset_monitor) + { + using web::json::value; + + auto data = make_nc_status_monitor(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints, enabled, overall_status, overall_status_message, status_reporting_delay); + + data[nmos::fields::nc::link_status] = value::number(link_status); + data[nmos::fields::nc::link_status_message] = value::string(link_status_message); + data[nmos::fields::nc::link_status_transition_counter] = value::number(0); + data[nmos::fields::nc::transmission_status] = value::number(transmission_status); + data[nmos::fields::nc::transmission_status_message] = value::string(transmission_status_message); + data[nmos::fields::nc::transmission_status_transition_counter] = value::number(0); + data[nmos::fields::nc::external_synchronization_status] = value::number(external_synchronization_status); + data[nmos::fields::nc::external_synchronization_status_message] = value::string(external_synchronization_status_message); + data[nmos::fields::nc::external_synchronization_status_transition_counter] = value::number(0); + data[nmos::fields::nc::synchronization_source_id] = synchronization_source_id; + data[nmos::fields::nc::essence_status] = value::number(essence_status); + data[nmos::fields::nc::essence_status_message] = value::string(essence_status_message); + data[nmos::fields::nc::essence_status_transition_counter] = value::number(0); + data[nmos::fields::nc::auto_reset_monitor] = value::boolean(auto_reset_monitor); + + // Pending status updates + data[nmos::fields::nc::monitor_activation_time] = value::number(0); + data[nmos::fields::nc::link_status_pending] = value::number(link_status); + data[nmos::fields::nc::link_status_message_pending] = value::string(link_status_message); + data[nmos::fields::nc::link_status_pending_received_time] = value::number(0); + data[nmos::fields::nc::transmission_status_pending] = value::number(transmission_status); + data[nmos::fields::nc::transmission_status_message_pending] = value::string(transmission_status_message); + data[nmos::fields::nc::transmission_status_pending_received_time] = value::number(0); + data[nmos::fields::nc::external_synchronization_status_pending] = value::number(external_synchronization_status); + data[nmos::fields::nc::external_synchronization_status_message_pending] = value::string(external_synchronization_status_message); + data[nmos::fields::nc::external_synchronization_status_pending_received_time] = value::number(0); + data[nmos::fields::nc::essence_status_pending] = value::number(essence_status); + data[nmos::fields::nc::essence_status_message_pending] = value::string(essence_status_message); + data[nmos::fields::nc::essence_status_pending_received_time] = value::number(0); + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager + web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + { + return make_nc_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager + web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, + const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, + const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause) + { + using web::json::value; + + auto data = details::make_nc_manager(nc_device_manager_class_id, oid, true, owner, U("DeviceManager"), user_label, description, touchpoints, runtime_property_constraints); + data[nmos::fields::nc::nc_version] = value::string(U("v1.0.0")); + data[nmos::fields::nc::manufacturer] = manufacturer; + data[nmos::fields::nc::product] = product; + data[nmos::fields::nc::serial_number] = value::string(serial_number); + data[nmos::fields::nc::user_inventory_code] = user_inventory_code; + data[nmos::fields::nc::device_name] = device_name; + data[nmos::fields::nc::device_role] = device_role; + data[nmos::fields::nc::operational_state] = operational_state; + data[nmos::fields::nc::reset_cause] = reset_cause; + data[nmos::fields::nc::message] = value::null(); + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager + web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state) + { + using web::json::value; + + auto data = make_nc_manager(nc_class_manager_class_id, oid, true, owner, U("ClassManager"), user_label, description, touchpoints, runtime_property_constraints); + + auto lock = control_protocol_state.read_lock(); + + // add control classes + data[nmos::fields::nc::control_classes] = value::array(); + auto& control_classes = data[nmos::fields::nc::control_classes]; + for (const auto& control_class : control_protocol_state.control_class_descriptors) + { + auto& ctl_class = control_class.second; + + auto method_descriptors = value::array(); + for (const auto& method_descriptor : ctl_class.method_descriptors) { web::json::push_back(method_descriptors, std::get<0>(method_descriptor)); } + + const auto class_description = ctl_class.fixed_role.is_null() + ? make_nc_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.property_descriptors, method_descriptors, ctl_class.event_descriptors) + : make_nc_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.fixed_role.as_string(), ctl_class.property_descriptors, method_descriptors, ctl_class.event_descriptors); + web::json::push_back(control_classes, class_description); + } + + // add datatypes + data[nmos::fields::nc::datatypes] = value::array(); + auto& datatypes = data[nmos::fields::nc::datatypes]; + for (const auto& datatype_descriptor : control_protocol_state.datatype_descriptors) + { + web::json::push_back(datatypes, datatype_descriptor.second.descriptor); + } + + return data; + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertychangedeventdata + web::json::value make_nc_property_changed_event_data(const nc_property_changed_event_data& property_changed_event_data) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::property_id, details::make_nc_property_id(property_changed_event_data.property_id) }, + { nmos::fields::nc::change_type, property_changed_event_data.change_type }, + { nmos::fields::nc::value, property_changed_event_data.value }, + { nmos::fields::nc::sequence_item_index, property_changed_event_data.sequence_item_index } + }, true + ); + } + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager + web::json::value make_nc_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value &user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + { + using web::json::value; + + auto data = make_nc_manager(nc_bulk_properties_manager_class_id, oid, true, owner, U("BulkPropertiesManager"), user_label, description, touchpoints, runtime_property_constraints); + + return data; + } + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesholder + web::json::value make_nc_bulk_properties_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::validation_fingerprint, validation_fingerprint }, + { nmos::fields::nc::values, object_properties_holders } + }, true + ); + } + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyholder + web::json::value make_nc_property_holder(const nc_property_id& property_id, const web::json::value& descriptor, const web::json::value& property_value) + { + using web::json::value; + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::id, make_nc_property_id(property_id)}, + { nmos::fields::nc::descriptor, descriptor}, + { nmos::fields::nc::value, property_value}, + }, true); + } + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiesholder + web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_holders, const web::json::array& dependency_paths, const web::json::array& allowed_members_classes, bool is_rebuildable) + { + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::path, web::json::value_from_elements(role_path)}, + { nmos::fields::nc::dependency_paths, web::json::value_from_elements(dependency_paths)}, + { nmos::fields::nc::allowed_members_classes, web::json::value_from_elements(allowed_members_classes)}, + { nmos::fields::nc::values, web::json::value_from_elements(property_holders)}, + { nmos::fields::nc::is_rebuildable, is_rebuildable} + }, true + ); + + } + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenotice + web::json::value make_nc_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message) + { + using web::json::value; + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::id, make_nc_property_id(property_id)}, + { nmos::fields::nc::name, value::string(name)}, + { nmos::fields::nc::notice_type, value::number(notice_type)}, + { nmos::fields::nc::notice_message, value::string(notice_message)} + }, true + ); + } + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiessetvalidation + web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const web::json::value& status_message) + { + using web::json::value; + using web::json::value_of; + + return value_of({ + { nmos::fields::nc::path, web::json::value_from_elements(role_path)}, + { nmos::fields::nc::status, value::number(status)}, + { nmos::fields::nc::notices, web::json::value_from_elements(notices)}, + { nmos::fields::nc::status_message, status_message} + }, true + ); + } } - web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const utility::string_t& parent_type, const web::json::value& constraints) + + // command message response + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#command-response-message-type + web::json::value make_control_protocol_response(int32_t handle, const web::json::value& method_result) { - using web::json::value; + using web::json::value_of; - return make_nc_datatype_descriptor_struct(value::string(description), name, fields, value::string(parent_type), constraints); + return value_of({ + { nmos::fields::nc::handle, handle }, + { nmos::fields::nc::result, method_result } + }); } - web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const web::json::value& constraints) + web::json::value make_control_protocol_command_response(const web::json::value& responses) { - using web::json::value; + using web::json::value_of; - return make_nc_datatype_descriptor_struct(value::string(description), name, fields, value::null(), constraints); + return value_of({ + { nmos::fields::nc::message_type, ncp_message_type::command_response }, + { nmos::fields::nc::responses, responses } + }); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptortypedef - // description can be null - // constraints can be null - web::json::value make_nc_datatype_typedef(const web::json::value& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints) + // subscription response + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#subscription-response-message-type + web::json::value make_control_protocol_subscription_response(const web::json::value& subscriptions) { - using web::json::value; - - auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Typedef, constraints); - data[nmos::fields::nc::parent_type] = value::string(parent_type); - data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); + using web::json::value_of; - return data; + return value_of({ + { nmos::fields::nc::message_type, ncp_message_type::subscription_response }, + { nmos::fields::nc::subscriptions, subscriptions } + }); } - web::json::value make_nc_datatype_typedef(const utility::string_t& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints) + + // notification + // See https://specs.amwa.tv/ms-05-01/branches/v1.0.x/docs/Core_Mechanisms.html#notification-messages + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#notification-message-type + web::json::value make_control_protocol_notification(nc_oid oid, const nc_event_id& event_id, const nc_property_changed_event_data& property_changed_event_data) { - using web::json::value; + using web::json::value_of; - return make_nc_datatype_typedef(value::string(description), name, is_sequence, parent_type, constraints); + return value_of({ + { nmos::fields::nc::oid, oid }, + { nmos::fields::nc::event_id, details::make_nc_event_id(event_id)}, + { nmos::fields::nc::event_data, details::make_nc_property_changed_event_data(property_changed_event_data) } + }); } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraints - web::json::value make_nc_property_constraints(const nc_property_id& property_id, const web::json::value& default_value) + web::json::value make_control_protocol_notification_message(const web::json::value& notifications) { using web::json::value_of; return value_of({ - { nmos::fields::nc::property_id, make_nc_property_id(property_id) }, - { nmos::fields::nc::default_value, default_value } + { nmos::fields::nc::message_type, ncp_message_type::notification }, + { nmos::fields::nc::notifications, notifications } }); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsnumber - web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, const web::json::value& default_value, const web::json::value& minimum, const web::json::value& maximum, const web::json::value& step) + // property changed notification event + // See https://specs.amwa.tv/ms-05-01/branches/v1.0.x/docs/Core_Mechanisms.html#the-propertychanged-event + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/NcObject.html#propertychanged-event + web::json::value make_property_changed_event(nc_oid oid, const std::vector& property_changed_event_data_list) { using web::json::value; - auto data = make_nc_property_constraints(property_id, default_value); - data[nmos::fields::nc::minimum] = minimum; - data[nmos::fields::nc::maximum] = maximum; - data[nmos::fields::nc::step] = step; - - return data; + auto notifications = value::array(); + for (auto& property_changed_event_data : property_changed_event_data_list) + { + web::json::push_back(notifications, make_control_protocol_notification(oid, nc_object_property_changed_event_id, property_changed_event_data)); + } + return make_control_protocol_notification_message(notifications); } - web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step) + + // error message + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#error-messages + web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message) { - using web::json::value; + using web::json::value_of; - return make_nc_property_constraints_number(property_id, value(default_value), value(minimum), value(maximum), value(step)); + return value_of({ + { nmos::fields::nc::message_type, ncp_message_type::error }, + { nmos::fields::nc::status, method_result.status}, + { nmos::fields::nc::error_message, error_message } + }); } - web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t minimum, uint64_t maximum, uint64_t step) + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject + web::json::value make_nc_object_properties() { using web::json::value; - return make_nc_property_constraints_number(property_id, value::null(), minimum, maximum, step); - } + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Static value. All instances of the same class will have the same identity value"), nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Object identifier"), nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE iff OID is hardwired into device"), nc_object_constant_oid_property_id, nmos::fields::nc::constant_oid, U("NcBoolean"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("OID of containing block. Can only ever be null for the root block"), nc_object_owner_property_id, nmos::fields::nc::owner, U("NcOid"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Role of object in the containing block"), nc_object_role_property_id, nmos::fields::nc::role, U("NcString"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Scribble strip"), nc_object_user_label_property_id, nmos::fields::nc::user_label, U("NcString"), false, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Touchpoints to other contexts"), nc_object_touchpoints_property_id, nmos::fields::nc::touchpoints, U("NcTouchpoint"), true, true, true, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Runtime property constraints"), nc_object_runtime_property_constraints_property_id, nmos::fields::nc::runtime_property_constraints, U("NcPropertyConstraints"), true, true, true, false, value::null())); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsstring - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const web::json::value& default_value, const web::json::value& max_characters, const web::json::value& pattern) + return properties; + } + web::json::value make_nc_object_methods() { using web::json::value; - auto data = make_nc_property_constraints(property_id, default_value); - data[nmos::fields::nc::max_characters] = max_characters; - data[nmos::fields::nc::pattern] = pattern; + auto methods = value::array(); + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get property value"), nc_object_get_method_id, U("Get"), U("NcMethodResultPropertyValue"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Set property value"), nc_object_set_method_id, U("Set"), U("NcMethodResult"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get sequence item"), nc_object_get_sequence_item_method_id, U("GetSequenceItem"), U("NcMethodResultPropertyValue"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Set sequence item value"), nc_object_set_sequence_item_method_id, U("SetSequenceItem"), U("NcMethodResult"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id,U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Add item to sequence"), nc_object_add_sequence_item_method_id, U("AddSequenceItem"), U("NcMethodResultId"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Delete sequence item"), nc_object_remove_sequence_item_method_id, U("RemoveSequenceItem"), U("NcMethodResult"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get sequence length"), nc_object_get_sequence_length_method_id, U("GetSequenceLength"), U("NcMethodResultLength"), parameters, false)); + } - return data; + return methods; } - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern) + web::json::value make_nc_object_events() { using web::json::value; - return make_nc_property_constraints_string(property_id, value::string(default_value), max_characters, value::string(pattern)); - } - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters, const nc_regex& pattern) - { - using web::json::value; + auto events = value::array(); + web::json::push_back(events, details::make_nc_event_descriptor(U("Property changed event"), nc_object_property_changed_event_id, U("PropertyChanged"), U("NcPropertyChangedEventData"), false)); - return make_nc_property_constraints_string(property_id, value::null(), max_characters, value::string(pattern)); + return events; } - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters) - { - using web::json::value; - return make_nc_property_constraints_string(property_id, value::null(), max_characters, value::null()); - } - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const nc_regex& pattern) + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock + web::json::value make_nc_block_properties() { using web::json::value; - return make_nc_property_constraints_string(property_id, value::null(), value::null(), value::string(pattern)); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraints - web::json::value make_nc_parameter_constraints(const web::json::value& default_value) - { - using web::json::value_of; + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE if block is functional"), nc_block_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptors of this block's members"), nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, value::null())); - return value_of({ - { nmos::fields::nc::default_value, default_value } - }); + return properties; } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber - web::json::value make_nc_parameter_constraints_number(const web::json::value& default_value, const web::json::value& minimum, const web::json::value& maximum, const web::json::value& step) + web::json::value make_nc_block_methods() { using web::json::value; - auto data = make_nc_parameter_constraints(default_value); - data[nmos::fields::nc::minimum] = minimum; - data[nmos::fields::nc::maximum] = maximum; - data[nmos::fields::nc::step] = step; + auto methods = value::array(); + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If recurse is set to true, nested members can be retrieved"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Gets descriptors of members of the block"), nc_block_get_member_descriptors_method_id, U("GetMemberDescriptors"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Relative path to search for (MUST not include the role of the block targeted by oid)"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds member(s) by path"), nc_block_find_members_by_path_method_id, U("FindMembersByPath"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Role text to search for"), nmos::fields::nc::role, U("NcString"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Signals if the comparison should be case sensitive"), nmos::fields::nc::case_sensitive, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("TRUE to only return exact matches"), nmos::fields::nc::match_whole_string, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("TRUE to search nested blocks"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds members with given role name or fragment"), nc_block_find_members_by_role_method_id, U("FindMembersByRole"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Class id to search for"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If TRUE it will also include derived class descriptors"), nmos::fields::nc::include_derived, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("TRUE to search nested blocks"), nmos::fields::nc::recurse,U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds members with given class id"), nc_block_find_members_by_class_id_method_id, U("FindMembersByClassId"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + } - return data; + return methods; } - web::json::value make_nc_parameter_constraints_number(uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step) + web::json::value make_nc_block_events() { using web::json::value; - return make_nc_parameter_constraints_number(value(default_value), value(minimum), value(maximum), value(step)); + return value::array(); } - web::json::value make_nc_parameter_constraints_number(uint64_t minimum, uint64_t maximum, uint64_t step) + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker + web::json::value make_nc_worker_properties() { using web::json::value; - return make_nc_parameter_constraints_number(value::null(), minimum, maximum, step); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring - web::json::value make_nc_parameter_constraints_string(const web::json::value& default_value, const web::json::value& max_characters, const web::json::value& pattern) - { - auto data = make_nc_parameter_constraints(default_value); - data[nmos::fields::nc::max_characters] = max_characters; - data[nmos::fields::nc::pattern] = pattern; + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE iff worker is enabled"), nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, value::null())); - return data; + return properties; } - web::json::value make_nc_parameter_constraints_string(const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern) + web::json::value make_nc_worker_methods() { using web::json::value; - return make_nc_parameter_constraints_string(value::string(default_value), max_characters, value::string(pattern)); + return value::array(); } - web::json::value make_nc_parameter_constraints_string(uint32_t max_characters, const nc_regex& pattern) + web::json::value make_nc_worker_events() { using web::json::value; - return make_nc_parameter_constraints_string(value::null(), max_characters, value::string(pattern)); + return value::array(); } - web::json::value make_nc_parameter_constraints_string(uint32_t max_characters) - { - using web::json::value; - return make_nc_parameter_constraints_string(value::null(), max_characters, value::null()); - } - web::json::value make_nc_parameter_constraints_string(const nc_regex& pattern) + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager + web::json::value make_nc_manager_properties() { using web::json::value; - return make_nc_parameter_constraints_string(value::null(), value::null(), value::string(pattern)); + return value::array(); } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresource - web::json::value make_nc_touchpoint_resource(const nc_touchpoint_resource& resource) + web::json::value make_nc_manager_methods() { - using web::json::value_of; + using web::json::value; - return value_of({ - { nmos::fields::nc::resource_type, resource.resource_type } - }); + return value::array(); } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresourcenmos - web::json::value make_nc_touchpoint_resource_nmos(const nc_touchpoint_resource_nmos& resource) + web::json::value make_nc_manager_events() { using web::json::value; - auto data = make_nc_touchpoint_resource(resource); - data[nmos::fields::nc::id] = value::string(resource.id); - - return data; + return value::array(); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresourcenmoschannelmapping - web::json::value make_nc_touchpoint_resource_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource) + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager + web::json::value make_nc_device_manager_properties() { using web::json::value; - auto data = make_nc_touchpoint_resource_nmos(resource); - data[nmos::fields::nc::io_id] = value::string(resource.io_id); - - return data; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpoint - web::json::value make_nc_touchpoint(const utility::string_t& context_namespace) - { - using web::json::value_of; + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Version of MS-05-02 that this device uses"), nc_device_manager_nc_version_property_id, nmos::fields::nc::nc_version, U("NcVersionCode"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Manufacturer descriptor"), nc_device_manager_manufacturer_property_id, nmos::fields::nc::manufacturer, U("NcManufacturer"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Product descriptor"), nc_device_manager_product_property_id, nmos::fields::nc::product, U("NcProduct"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Serial number"), nc_device_manager_serial_number_property_id, nmos::fields::nc::serial_number, U("NcString"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Asset tracking identifier (user specified)"), nc_device_manager_user_inventory_code_property_id, nmos::fields::nc::user_inventory_code, U("NcString"), false, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Name of this device in the application. Instance name, not product name"), nc_device_manager_device_name_property_id, nmos::fields::nc::device_name, U("NcString"), false, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Role of this device in the application"), nc_device_manager_device_role_property_id, nmos::fields::nc::device_role, U("NcString"), false, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Device operational state"), nc_device_manager_operational_state_property_id, nmos::fields::nc::operational_state, U("NcDeviceOperationalState"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Reason for most recent reset"), nc_device_manager_reset_cause_property_id, nmos::fields::nc::reset_cause, U("NcResetCause"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Arbitrary message from dev to controller"), nc_device_manager_message_property_id, nmos::fields::nc::message, U("NcString"), true, true, false, false, value::null())); - return value_of({ - { nmos::fields::nc::context_namespace, context_namespace } - }); + return properties; } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointnmos - web::json::value make_nc_touchpoint_nmos(const nc_touchpoint_resource_nmos& resource) + web::json::value make_nc_device_manager_methods() { - auto data = make_nc_touchpoint(U("x-nmos")); - data[nmos::fields::nc::resource] = make_nc_touchpoint_resource_nmos(resource); + using web::json::value; - return data; + return value::array(); } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointnmoschannelmapping - web::json::value make_nc_touchpoint_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource) + web::json::value make_nc_device_manager_events() { - auto data = make_nc_touchpoint(U("x-nmos/channelmapping")); - data[nmos::fields::nc::resource] = make_nc_touchpoint_resource_nmos_channel_mapping(resource); + using web::json::value; - return data; + return value::array(); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject - web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager + web::json::value make_nc_class_manager_properties() { using web::json::value; - const auto id = utility::conversions::details::to_string_t(oid); - auto data = make_resource_core(id, user_label.is_null() ? U("") : user_label.as_string(), description); // required for nmos::resource - data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); - data[nmos::fields::nc::oid] = oid; - data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); - data[nmos::fields::nc::owner] = owner; - data[nmos::fields::nc::role] = value::string(role); - data[nmos::fields::nc::user_label] = user_label; - data[nmos::fields::nc::touchpoints] = touchpoints; - data[nmos::fields::nc::runtime_property_constraints] = runtime_property_constraints; // level 2 runtime constraints. See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptions of all control classes in the device (descriptors do not contain inherited elements)"), nc_class_manager_control_classes_property_id, nmos::fields::nc::control_classes, U("NcClassDescriptor"), true, false, true, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptions of all data types in the device (descriptors do not contain inherited elements)"), nc_class_manager_datatypes_property_id, nmos::fields::nc::datatypes, U("NcDatatypeDescriptor"), true, false, true, false, value::null())); - // IS-14 metadata fields - // These fields are "invisible" as they are not part of the NcObject definition - // use make_rebuildable function to declare an control protocol resource rebuildable - data[nmos::fields::nc::is_rebuildable] = value::boolean(false); - // use allowed_member_classes to restrict the types of object that an NcBlock can contain - data[nmos::fields::nc::allowed_members_classes] = value::array(); - // use to indicate dependencies of an object in the device model - data[nmos::fields::nc::dependency_paths] = value::array(); - - return data; + return properties; } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock - web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members) + web::json::value make_nc_class_manager_methods() { using web::json::value; - auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); - data[nmos::fields::nc::enabled] = value::boolean(enabled); - data[nmos::fields::nc::members] = members; + auto methods = value::array(); + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("class ID"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::include_inherited, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get a single class descriptor"), nc_class_manager_get_control_class_method_id, U("GetControlClass"), U("NcMethodResultClassDescriptor"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("name of datatype"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::include_inherited, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get a single datatype descriptor"), nc_class_manager_get_datatype_method_id, U("GetDatatype"), U("NcMethodResultDatatypeDescriptor"), parameters, false)); + } - return data; + return methods; } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker - web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled) + web::json::value make_nc_class_manager_events() { using web::json::value; - auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); - data[nmos::fields::nc::enabled] = value::boolean(enabled); - - return data; + return value::array(); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstatusmonitor - web::json::value make_nc_status_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, uint64_t status_reporting_delay) + web::json::value make_nc_status_monitor_properties() { using web::json::value; - auto data = make_nc_worker(class_id, oid, constant_oid, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, enabled); - data[nmos::fields::nc::overall_status] = value::number(overall_status); - data[nmos::fields::nc::overall_status_message] = value::string(overall_status_message); - data[nmos::fields::nc::status_reporting_delay] = value::number(status_reporting_delay); + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Overall status property"), nc_status_monitor_overall_status_property_id, nmos::fields::nc::overall_status, U("NcOverallStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Overall status message property"), nc_status_monitor_overall_status_message_property_id, nmos::fields::nc::overall_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Status reporting delay property (in seconds, default is 3s and 0 means no delay)"), nc_status_monitor_status_reporting_delay, nmos::fields::nc::status_reporting_delay, U("NcUint32"), false, false, false, false, value::null())); - return data; + return properties; } + web::json::value make_nc_status_monitor_methods() + { + using web::json::value; - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor - web::json::value make_receiver_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, nc_link_status::status link_status, const utility::string_t& link_status_message, nc_connection_status::status connection_status, const utility::string_t& connection_status_message, nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, const web::json::value& synchronization_source_id, nc_stream_status::status stream_status, const utility::string_t& stream_status_message, uint32_t status_reporting_delay, bool auto_reset_monitor) - { - using web::json::value; - - auto data = make_nc_status_monitor(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints, enabled, overall_status, overall_status_message, status_reporting_delay); - - data[nmos::fields::nc::link_status] = value::number(link_status); - data[nmos::fields::nc::link_status_message] = value::string(link_status_message); - data[nmos::fields::nc::link_status_transition_counter] = value::number(0); - data[nmos::fields::nc::connection_status] = value::number(connection_status); - data[nmos::fields::nc::connection_status_message] = value::string(connection_status_message); - data[nmos::fields::nc::connection_status_transition_counter] = value::number(0); - data[nmos::fields::nc::external_synchronization_status] = value::number(external_synchronization_status); - data[nmos::fields::nc::external_synchronization_status_message] = value::string(external_synchronization_status_message); - data[nmos::fields::nc::external_synchronization_status_transition_counter] = value::number(0); - data[nmos::fields::nc::synchronization_source_id] = synchronization_source_id; - data[nmos::fields::nc::stream_status] = value::number(stream_status); - data[nmos::fields::nc::stream_status_message] = value::string(stream_status_message); - data[nmos::fields::nc::stream_status_transition_counter] = value::number(0); - data[nmos::fields::nc::auto_reset_monitor] = value::boolean(auto_reset_monitor); - - // Pending status updates - data[nmos::fields::nc::monitor_activation_time] = value::number(0); - data[nmos::fields::nc::link_status_pending] = value::number(link_status); - data[nmos::fields::nc::link_status_message_pending] = value::string(link_status_message); - data[nmos::fields::nc::link_status_pending_received_time] = value::number(0); - data[nmos::fields::nc::connection_status_pending] = value::number(connection_status); - data[nmos::fields::nc::connection_status_message_pending] = value::string(connection_status_message); - data[nmos::fields::nc::connection_status_pending_received_time] = value::number(0); - data[nmos::fields::nc::external_synchronization_status_pending] = value::number(external_synchronization_status); - data[nmos::fields::nc::external_synchronization_status_message_pending] = value::string(external_synchronization_status_message); - data[nmos::fields::nc::external_synchronization_status_pending_received_time] = value::number(0); - data[nmos::fields::nc::stream_status_pending] = value::number(stream_status); - data[nmos::fields::nc::stream_status_message_pending] = value::string(stream_status_message); - data[nmos::fields::nc::stream_status_pending_received_time] = value::number(0); - - return data; + return value::array(); } + web::json::value make_nc_status_monitor_events() + { + using web::json::value; - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsendermonitor - web::json::value make_sender_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, nc_link_status::status link_status, const utility::string_t& link_status_message, nc_transmission_status::status transmission_status, const utility::string_t& transmission_status_message, nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, const web::json::value& synchronization_source_id, nc_essence_status::status essence_status, const utility::string_t& essence_status_message, uint32_t status_reporting_delay, bool auto_reset_monitor) - { - using web::json::value; - - auto data = make_nc_status_monitor(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints, enabled, overall_status, overall_status_message, status_reporting_delay); - - data[nmos::fields::nc::link_status] = value::number(link_status); - data[nmos::fields::nc::link_status_message] = value::string(link_status_message); - data[nmos::fields::nc::link_status_transition_counter] = value::number(0); - data[nmos::fields::nc::transmission_status] = value::number(transmission_status); - data[nmos::fields::nc::transmission_status_message] = value::string(transmission_status_message); - data[nmos::fields::nc::transmission_status_transition_counter] = value::number(0); - data[nmos::fields::nc::external_synchronization_status] = value::number(external_synchronization_status); - data[nmos::fields::nc::external_synchronization_status_message] = value::string(external_synchronization_status_message); - data[nmos::fields::nc::external_synchronization_status_transition_counter] = value::number(0); - data[nmos::fields::nc::synchronization_source_id] = synchronization_source_id; - data[nmos::fields::nc::essence_status] = value::number(essence_status); - data[nmos::fields::nc::essence_status_message] = value::string(essence_status_message); - data[nmos::fields::nc::essence_status_transition_counter] = value::number(0); - data[nmos::fields::nc::auto_reset_monitor] = value::boolean(auto_reset_monitor); - - // Pending status updates - data[nmos::fields::nc::monitor_activation_time] = value::number(0); - data[nmos::fields::nc::link_status_pending] = value::number(link_status); - data[nmos::fields::nc::link_status_message_pending] = value::string(link_status_message); - data[nmos::fields::nc::link_status_pending_received_time] = value::number(0); - data[nmos::fields::nc::transmission_status_pending] = value::number(transmission_status); - data[nmos::fields::nc::transmission_status_message_pending] = value::string(transmission_status_message); - data[nmos::fields::nc::transmission_status_pending_received_time] = value::number(0); - data[nmos::fields::nc::external_synchronization_status_pending] = value::number(external_synchronization_status); - data[nmos::fields::nc::external_synchronization_status_message_pending] = value::string(external_synchronization_status_message); - data[nmos::fields::nc::external_synchronization_status_pending_received_time] = value::number(0); - data[nmos::fields::nc::essence_status_pending] = value::number(essence_status); - data[nmos::fields::nc::essence_status_message_pending] = value::string(essence_status_message); - data[nmos::fields::nc::essence_status_pending_received_time] = value::number(0); - - return data; + return value::array(); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager - web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor + web::json::value make_nc_receiver_monitor_properties() { - return make_nc_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); - } + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager - web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, - const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, - const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause) + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status property"), nc_receiver_monitor_link_status_property_id, nmos::fields::nc::link_status, U("NcLinkStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status message property"), nc_receiver_monitor_link_status_message_property_id, nmos::fields::nc::link_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status transition counter property"), nc_receiver_monitor_link_status_transition_counter_property_id, nmos::fields::nc::link_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status property"), nc_receiver_monitor_connection_status_property_id, nmos::fields::nc::connection_status, U("NcConnectionStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status message property"), nc_receiver_monitor_connection_status_message_property_id, nmos::fields::nc::connection_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status transition counter property"), nc_receiver_monitor_connection_status_transition_counter_property_id, nmos::fields::nc::connection_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status property"), nc_receiver_monitor_external_synchronization_status_property_id, nmos::fields::nc::external_synchronization_status, U("NcSynchronizationStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status message property"), nc_receiver_monitor_external_synchronization_status_message_property_id, nmos::fields::nc::external_synchronization_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status transition counter property"), nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, nmos::fields::nc::external_synchronization_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Synchronization source id property"), nc_receiver_monitor_synchronization_source_id_property_id, nmos::fields::nc::synchronization_source_id, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Stream status property"), nc_receiver_monitor_stream_status_property_id, nmos::fields::nc::stream_status, U("NcStreamStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Stream status message property"), nc_receiver_monitor_stream_status_message_property_id, nmos::fields::nc::stream_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Stream status property transition counters"), nc_receiver_monitor_stream_status_transition_counter_property_id, nmos::fields::nc::stream_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Automatic reset counters and messages property (default: true)"), nc_receiver_monitor_auto_reset_monitor_property_id, nmos::fields::nc::auto_reset_monitor, U("NcBoolean"), false, false, false, false, value::null())); + + return properties; + } + web::json::value make_nc_receiver_monitor_methods() { using web::json::value; - auto data = details::make_nc_manager(nc_device_manager_class_id, oid, true, owner, U("DeviceManager"), user_label, description, touchpoints, runtime_property_constraints); - data[nmos::fields::nc::nc_version] = value::string(U("v1.0.0")); - data[nmos::fields::nc::manufacturer] = manufacturer; - data[nmos::fields::nc::product] = product; - data[nmos::fields::nc::serial_number] = value::string(serial_number); - data[nmos::fields::nc::user_inventory_code] = user_inventory_code; - data[nmos::fields::nc::device_name] = device_name; - data[nmos::fields::nc::device_role] = device_role; - data[nmos::fields::nc::operational_state] = operational_state; - data[nmos::fields::nc::reset_cause] = reset_cause; - data[nmos::fields::nc::message] = value::null(); + auto methods = value::array(); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Gets the lost packet counters"), nc_receiver_monitor_get_lost_packet_counters_method_id, U("GetLostPacketCounters"), U("NcMethodResultCounters"), value::array(), false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Gets the late packet counters"), nc_receiver_monitor_get_late_packet_counters_method_id, U("GetLatePacketCounters"), U("NcMethodResultCounters"), value::array(), false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Resets ALL counters"), nc_receiver_monitor_reset_monitor_method_id, U("ResetCountersAndMessages"), U("NcMethodResult"), value::array(), false)); - return data; + return methods; } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state) + web::json::value make_nc_receiver_monitor_events() { using web::json::value; - auto data = make_nc_manager(nc_class_manager_class_id, oid, true, owner, U("ClassManager"), user_label, description, touchpoints, runtime_property_constraints); - - auto lock = control_protocol_state.read_lock(); - - // add control classes - data[nmos::fields::nc::control_classes] = value::array(); - auto& control_classes = data[nmos::fields::nc::control_classes]; - for (const auto& control_class : control_protocol_state.control_class_descriptors) - { - auto& ctl_class = control_class.second; - - auto method_descriptors = value::array(); - for (const auto& method_descriptor : ctl_class.method_descriptors) { web::json::push_back(method_descriptors, std::get<0>(method_descriptor)); } - - const auto class_description = ctl_class.fixed_role.is_null() - ? make_nc_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.property_descriptors, method_descriptors, ctl_class.event_descriptors) - : make_nc_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.fixed_role.as_string(), ctl_class.property_descriptors, method_descriptors, ctl_class.event_descriptors); - web::json::push_back(control_classes, class_description); - } - - // add datatypes - data[nmos::fields::nc::datatypes] = value::array(); - auto& datatypes = data[nmos::fields::nc::datatypes]; - for (const auto& datatype_descriptor : control_protocol_state.datatype_descriptors) - { - web::json::push_back(datatypes, datatype_descriptor.second.descriptor); - } - - return data; + return value::array(); } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertychangedeventdata - web::json::value make_nc_property_changed_event_data(const nc_property_changed_event_data& property_changed_event_data) + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsendermonitor + web::json::value make_nc_sender_monitor_properties() { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::property_id, details::make_nc_property_id(property_changed_event_data.property_id) }, - { nmos::fields::nc::change_type, property_changed_event_data.change_type }, - { nmos::fields::nc::value, property_changed_event_data.value }, - { nmos::fields::nc::sequence_item_index, property_changed_event_data.sequence_item_index } - }, true - ); - } + using web::json::value; - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager - web::json::value make_nc_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value &user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status property"), nc_sender_monitor_link_status_property_id, nmos::fields::nc::link_status, U("NcLinkStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status message property"), nc_sender_monitor_link_status_message_property_id, nmos::fields::nc::link_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status transition counter property"), nc_sender_monitor_link_status_transition_counter_property_id, nmos::fields::nc::link_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Transmission status property"), nc_sender_monitor_transmission_status_property_id, nmos::fields::nc::transmission_status, U("NcTransmissionStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Transmission status message property"), nc_sender_monitor_transmission_status_message_property_id, nmos::fields::nc::transmission_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Transmission status transition counter property"), nc_sender_monitor_transmission_status_transition_counter_property_id, nmos::fields::nc::transmission_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status property"), nc_sender_monitor_external_synchronization_status_property_id, nmos::fields::nc::external_synchronization_status, U("NcSynchronizationStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status message property"), nc_sender_monitor_external_synchronization_status_message_property_id, nmos::fields::nc::external_synchronization_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status transition counter property"), nc_sender_monitor_external_synchronization_status_transition_counter_property_id, nmos::fields::nc::external_synchronization_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Synchronization source id property"), nc_sender_monitor_synchronization_source_id_property_id, nmos::fields::nc::synchronization_source_id, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Essence status property"), nc_sender_monitor_essence_status_property_id, nmos::fields::nc::essence_status, U("NcEssenceStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Essence status message property"), nc_sender_monitor_essence_status_message_property_id, nmos::fields::nc::essence_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Essence status property transition counters"), nc_sender_monitor_essence_status_transition_counter_property_id, nmos::fields::nc::essence_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Automatic reset counters and messages property (default: true)"), nc_sender_monitor_auto_reset_monitor_property_id, nmos::fields::nc::auto_reset_monitor, U("NcBoolean"), false, false, false, false, value::null())); + + return properties; + } + web::json::value make_nc_sender_monitor_methods() { using web::json::value; - auto data = make_nc_manager(nc_bulk_properties_manager_class_id, oid, true, owner, U("BulkPropertiesManager"), user_label, description, touchpoints, runtime_property_constraints); - - return data; - } - - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesholder - web::json::value make_nc_bulk_properties_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders) - { - using web::json::value_of; + auto methods = value::array(); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Gets the transmission error counters"), nc_sender_monitor_get_transmission_error_counters_method_id, U("GetTransmissionErrorCounters"), U("NcMethodResultCounters"), value::array(), false)); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Resets ALL counters"), nc_sender_monitor_reset_monitor_method_id, U("ResetCountersAndMessages"), U("NcMethodResult"), value::array(), false)); - return value_of({ - { nmos::fields::nc::validation_fingerprint, validation_fingerprint }, - { nmos::fields::nc::values, object_properties_holders } - }, true - ); + return methods; } - - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyholder - web::json::value make_nc_property_holder(const nc_property_id& property_id, const web::json::value& descriptor, const web::json::value& property_value) + web::json::value make_nc_sender_monitor_events() { using web::json::value; - using web::json::value_of; - return value_of({ - { nmos::fields::nc::id, make_nc_property_id(property_id)}, - { nmos::fields::nc::descriptor, descriptor}, - { nmos::fields::nc::value, property_value}, - }, true); + return value::array(); } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiesholder - web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_holders, const web::json::array& dependency_paths, const web::json::array& allowed_members_classes, bool is_rebuildable) + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon + web::json::value make_nc_ident_beacon_properties() { - using web::json::value_of; + using web::json::value; - return value_of({ - { nmos::fields::nc::path, web::json::value_from_elements(role_path)}, - { nmos::fields::nc::dependency_paths, web::json::value_from_elements(dependency_paths)}, - { nmos::fields::nc::allowed_members_classes, web::json::value_from_elements(allowed_members_classes)}, - { nmos::fields::nc::values, web::json::value_from_elements(property_holders)}, - { nmos::fields::nc::is_rebuildable, is_rebuildable} - }, true - ); + auto properties = value::array(); + web::json::push_back(properties, details::make_nc_property_descriptor(U("Indicator active state"), nc_ident_beacon_active_property_id, nmos::fields::nc::active, U("NcBoolean"), false, false, false, false, value::null())); + return properties; } - - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenotice - web::json::value make_nc_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message) + web::json::value make_nc_ident_beacon_methods() { using web::json::value; - using web::json::value_of; - return value_of({ - { nmos::fields::nc::id, make_nc_property_id(property_id)}, - { nmos::fields::nc::name, value::string(name)}, - { nmos::fields::nc::notice_type, value::number(notice_type)}, - { nmos::fields::nc::notice_message, value::string(notice_message)} - }, true - ); + return value::array(); } - - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiessetvalidation - web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const web::json::value& status_message) + web::json::value make_nc_ident_beacon_events() { using web::json::value; - using web::json::value_of; - return value_of({ - { nmos::fields::nc::path, web::json::value_from_elements(role_path)}, - { nmos::fields::nc::status, value::number(status)}, - { nmos::fields::nc::notices, web::json::value_from_elements(notices)}, - { nmos::fields::nc::status_message, status_message} - }, true - ); + return value::array(); } - } - - // command message response - // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#command-response-message-type - web::json::value make_control_protocol_response(int32_t handle, const web::json::value& method_result) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::handle, handle }, - { nmos::fields::nc::result, method_result } - }); - } - web::json::value make_control_protocol_command_response(const web::json::value& responses) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::message_type, ncp_message_type::command_response }, - { nmos::fields::nc::responses, responses } - }); - } - - // subscription response - // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#subscription-response-message-type - web::json::value make_control_protocol_subscription_response(const web::json::value& subscriptions) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::message_type, ncp_message_type::subscription_response }, - { nmos::fields::nc::subscriptions, subscriptions } - }); - } - - // notification - // See https://specs.amwa.tv/ms-05-01/branches/v1.0.x/docs/Core_Mechanisms.html#notification-messages - // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#notification-message-type - web::json::value make_control_protocol_notification(nc_oid oid, const nc_event_id& event_id, const nc_property_changed_event_data& property_changed_event_data) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::oid, oid }, - { nmos::fields::nc::event_id, details::make_nc_event_id(event_id)}, - { nmos::fields::nc::event_data, details::make_nc_property_changed_event_data(property_changed_event_data) } - }); - } - web::json::value make_control_protocol_notification_message(const web::json::value& notifications) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::message_type, ncp_message_type::notification }, - { nmos::fields::nc::notifications, notifications } - }); - } - - // property changed notification event - // See https://specs.amwa.tv/ms-05-01/branches/v1.0.x/docs/Core_Mechanisms.html#the-propertychanged-event - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/NcObject.html#propertychanged-event - web::json::value make_property_changed_event(nc_oid oid, const std::vector& property_changed_event_data_list) - { - using web::json::value; - auto notifications = value::array(); - for (auto& property_changed_event_data : property_changed_event_data_list) + // Device configuration classes + // NcBulkPropertiesManager + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager + web::json::value make_nc_bulk_properties_manager_properties() { - web::json::push_back(notifications, make_control_protocol_notification(oid, nc_object_property_changed_event_id, property_changed_event_data)); - } - return make_control_protocol_notification_message(notifications); - } - - // error message - // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#error-messages - web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message) - { - using web::json::value_of; - - return value_of({ - { nmos::fields::nc::message_type, ncp_message_type::error }, - { nmos::fields::nc::status, method_result.status}, - { nmos::fields::nc::error_message, error_message } - }); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject - web::json::value make_nc_object_properties() - { - using web::json::value; - - auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Static value. All instances of the same class will have the same identity value"), nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Object identifier"), nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE iff OID is hardwired into device"), nc_object_constant_oid_property_id, nmos::fields::nc::constant_oid, U("NcBoolean"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("OID of containing block. Can only ever be null for the root block"), nc_object_owner_property_id, nmos::fields::nc::owner, U("NcOid"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Role of object in the containing block"), nc_object_role_property_id, nmos::fields::nc::role, U("NcString"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Scribble strip"), nc_object_user_label_property_id, nmos::fields::nc::user_label, U("NcString"), false, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Touchpoints to other contexts"), nc_object_touchpoints_property_id, nmos::fields::nc::touchpoints, U("NcTouchpoint"), true, true, true, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Runtime property constraints"), nc_object_runtime_property_constraints_property_id, nmos::fields::nc::runtime_property_constraints, U("NcPropertyConstraints"), true, true, true, false, value::null())); - - return properties; - } - web::json::value make_nc_object_methods() - { - using web::json::value; + using web::json::value; - auto methods = value::array(); - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get property value"), nc_object_get_method_id, U("Get"), U("NcMethodResultPropertyValue"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Set property value"), nc_object_set_method_id, U("Set"), U("NcMethodResult"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get sequence item"), nc_object_get_sequence_item_method_id, U("GetSequenceItem"), U("NcMethodResultPropertyValue"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Set sequence item value"), nc_object_set_sequence_item_method_id, U("SetSequenceItem"), U("NcMethodResult"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id,U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Add item to sequence"), nc_object_add_sequence_item_method_id, U("AddSequenceItem"), U("NcMethodResultId"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Delete sequence item"), nc_object_remove_sequence_item_method_id, U("RemoveSequenceItem"), U("NcMethodResult"), parameters, false)); + return value::array(); } + web::json::value make_nc_bulk_properties_manager_methods() { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get sequence length"), nc_object_get_sequence_length_method_id, U("GetSequenceLength"), U("NcMethodResultLength"), parameters, false)); - } - - return methods; - } - web::json::value make_nc_object_events() - { - using web::json::value; - - auto events = value::array(); - web::json::push_back(events, details::make_nc_event_descriptor(U("Property changed event"), nc_object_property_changed_event_id, U("PropertyChanged"), U("NcPropertyChangedEventData"), false)); - - return events; - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock - web::json::value make_nc_block_properties() - { - using web::json::value; - - auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE if block is functional"), nc_block_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptors of this block's members"), nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, value::null())); + using web::json::value; - return properties; - } - web::json::value make_nc_block_methods() - { - using web::json::value; + auto methods = value::array(); + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The target role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will return properties on specified path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true, property holders returned will contain non-null property descriptors and for full backups the ClassManager role path will also be included"), nmos::fields::nc::include_descriptors, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Get bulk object properties by given path"), nc_bulk_properties_manager_get_properties_by_path_method_id, U("GetPropertiesByPath"), U("NcMethodResultBulkPropertiesHolder"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkPropertiesHolder"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Defines the restore mode to be applied"), nmos::fields::nc::restore_mode, U("NcRestoreMode"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Validate bulk properties for setting by given paths"), nc_bulk_properties_manager_validate_set_properties_by_path_method_id, U("ValidateSetPropertiesByPath"), U("NcMethodResultObjectPropertiesSetValidation"), parameters, false)); + } + { + auto parameters = value::array(); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkPropertiesHolder"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Defines the restore mode to be applied"), nmos::fields::nc::restore_mode, U("NcRestoreMode"), false, false, value::null())); + web::json::push_back(methods, details::make_nc_method_descriptor(U("Set bulk properties for setting by given paths"), nc_bulk_properties_manager_set_properties_by_path_method_id, U("SetPropertiesByPath"), U("NcMethodResultObjectPropertiesSetValidation"), parameters, false)); + } - auto methods = value::array(); - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If recurse is set to true, nested members can be retrieved"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Gets descriptors of members of the block"), nc_block_get_member_descriptors_method_id, U("GetMemberDescriptors"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Relative path to search for (MUST not include the role of the block targeted by oid)"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds member(s) by path"), nc_block_find_members_by_path_method_id, U("FindMembersByPath"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Role text to search for"), nmos::fields::nc::role, U("NcString"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Signals if the comparison should be case sensitive"), nmos::fields::nc::case_sensitive, U("NcBoolean"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("TRUE to only return exact matches"), nmos::fields::nc::match_whole_string, U("NcBoolean"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("TRUE to search nested blocks"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds members with given role name or fragment"), nc_block_find_members_by_role_method_id, U("FindMembersByRole"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + return methods; } + web::json::value make_nc_bulk_properties_manager_events() { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Class id to search for"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If TRUE it will also include derived class descriptors"), nmos::fields::nc::include_derived, U("NcBoolean"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("TRUE to search nested blocks"), nmos::fields::nc::recurse,U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds members with given class id"), nc_block_find_members_by_class_id_method_id, U("FindMembersByClassId"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); - } - - return methods; - } - web::json::value make_nc_block_events() - { - using web::json::value; - - return value::array(); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker - web::json::value make_nc_worker_properties() - { - using web::json::value; - - auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE iff worker is enabled"), nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, value::null())); - - return properties; - } - web::json::value make_nc_worker_methods() - { - using web::json::value; - - return value::array(); - } - web::json::value make_nc_worker_events() - { - using web::json::value; - - return value::array(); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager - web::json::value make_nc_manager_properties() - { - using web::json::value; - - return value::array(); - } - web::json::value make_nc_manager_methods() - { - using web::json::value; - - return value::array(); - } - web::json::value make_nc_manager_events() - { - using web::json::value; - - return value::array(); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager - web::json::value make_nc_device_manager_properties() - { - using web::json::value; - - auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Version of MS-05-02 that this device uses"), nc_device_manager_nc_version_property_id, nmos::fields::nc::nc_version, U("NcVersionCode"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Manufacturer descriptor"), nc_device_manager_manufacturer_property_id, nmos::fields::nc::manufacturer, U("NcManufacturer"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Product descriptor"), nc_device_manager_product_property_id, nmos::fields::nc::product, U("NcProduct"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Serial number"), nc_device_manager_serial_number_property_id, nmos::fields::nc::serial_number, U("NcString"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Asset tracking identifier (user specified)"), nc_device_manager_user_inventory_code_property_id, nmos::fields::nc::user_inventory_code, U("NcString"), false, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Name of this device in the application. Instance name, not product name"), nc_device_manager_device_name_property_id, nmos::fields::nc::device_name, U("NcString"), false, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Role of this device in the application"), nc_device_manager_device_role_property_id, nmos::fields::nc::device_role, U("NcString"), false, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Device operational state"), nc_device_manager_operational_state_property_id, nmos::fields::nc::operational_state, U("NcDeviceOperationalState"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Reason for most recent reset"), nc_device_manager_reset_cause_property_id, nmos::fields::nc::reset_cause, U("NcResetCause"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Arbitrary message from dev to controller"), nc_device_manager_message_property_id, nmos::fields::nc::message, U("NcString"), true, true, false, false, value::null())); - - return properties; - } - web::json::value make_nc_device_manager_methods() - { - using web::json::value; - - return value::array(); - } - web::json::value make_nc_device_manager_events() - { - using web::json::value; - - return value::array(); - } - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager_properties() - { - using web::json::value; - - auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptions of all control classes in the device (descriptors do not contain inherited elements)"), nc_class_manager_control_classes_property_id, nmos::fields::nc::control_classes, U("NcClassDescriptor"), true, false, true, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptions of all data types in the device (descriptors do not contain inherited elements)"), nc_class_manager_datatypes_property_id, nmos::fields::nc::datatypes, U("NcDatatypeDescriptor"), true, false, true, false, value::null())); - - return properties; - } - web::json::value make_nc_class_manager_methods() - { - using web::json::value; + using web::json::value; - auto methods = value::array(); - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("class ID"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::include_inherited, U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get a single class descriptor"), nc_class_manager_get_control_class_method_id, U("GetControlClass"), U("NcMethodResultClassDescriptor"), parameters, false)); - } - { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("name of datatype"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::include_inherited, U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get a single datatype descriptor"), nc_class_manager_get_datatype_method_id, U("GetDatatype"), U("NcMethodResultDatatypeDescriptor"), parameters, false)); + return value::array(); } - return methods; - } - web::json::value make_nc_class_manager_events() - { - using web::json::value; - - return value::array(); - } - - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstatusmonitor - web::json::value make_nc_status_monitor_properties() - { - using web::json::value; - - auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Overall status property"), nc_status_monitor_overall_status_property_id, nmos::fields::nc::overall_status, U("NcOverallStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Overall status message property"), nc_status_monitor_overall_status_message_property_id, nmos::fields::nc::overall_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Status reporting delay property (in seconds, default is 3s and 0 means no delay)"), nc_status_monitor_status_reporting_delay, nmos::fields::nc::status_reporting_delay, U("NcUint32"), false, false, false, false, value::null())); - - return properties; - } - web::json::value make_nc_status_monitor_methods() - { - using web::json::value; - - return value::array(); - } - web::json::value make_nc_status_monitor_events() - { - using web::json::value; - - return value::array(); - } - - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor - web::json::value make_nc_receiver_monitor_properties() - { - using web::json::value; - - auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status property"), nc_receiver_monitor_link_status_property_id, nmos::fields::nc::link_status, U("NcLinkStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status message property"), nc_receiver_monitor_link_status_message_property_id, nmos::fields::nc::link_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status transition counter property"), nc_receiver_monitor_link_status_transition_counter_property_id, nmos::fields::nc::link_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status property"), nc_receiver_monitor_connection_status_property_id, nmos::fields::nc::connection_status, U("NcConnectionStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status message property"), nc_receiver_monitor_connection_status_message_property_id, nmos::fields::nc::connection_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status transition counter property"), nc_receiver_monitor_connection_status_transition_counter_property_id, nmos::fields::nc::connection_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status property"), nc_receiver_monitor_external_synchronization_status_property_id, nmos::fields::nc::external_synchronization_status, U("NcSynchronizationStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status message property"), nc_receiver_monitor_external_synchronization_status_message_property_id, nmos::fields::nc::external_synchronization_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status transition counter property"), nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, nmos::fields::nc::external_synchronization_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Synchronization source id property"), nc_receiver_monitor_synchronization_source_id_property_id, nmos::fields::nc::synchronization_source_id, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Stream status property"), nc_receiver_monitor_stream_status_property_id, nmos::fields::nc::stream_status, U("NcStreamStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Stream status message property"), nc_receiver_monitor_stream_status_message_property_id, nmos::fields::nc::stream_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Stream status property transition counters"), nc_receiver_monitor_stream_status_transition_counter_property_id, nmos::fields::nc::stream_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Automatic reset counters and messages property (default: true)"), nc_receiver_monitor_auto_reset_monitor_property_id, nmos::fields::nc::auto_reset_monitor, U("NcBoolean"), false, false, false, false, value::null())); - - return properties; - } - web::json::value make_nc_receiver_monitor_methods() - { - using web::json::value; - - auto methods = value::array(); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Gets the lost packet counters"), nc_receiver_monitor_get_lost_packet_counters_method_id, U("GetLostPacketCounters"), U("NcMethodResultCounters"), value::array(), false)); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Gets the late packet counters"), nc_receiver_monitor_get_late_packet_counters_method_id, U("GetLatePacketCounters"), U("NcMethodResultCounters"), value::array(), false)); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Resets ALL counters"), nc_receiver_monitor_reset_monitor_method_id, U("ResetCountersAndMessages"), U("NcMethodResult"), value::array(), false)); - - return methods; - } - web::json::value make_nc_receiver_monitor_events() - { - using web::json::value; - - return value::array(); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.html + web::json::value make_nc_object_class() + { + using web::json::value; - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsendermonitor - web::json::value make_nc_sender_monitor_properties() - { - using web::json::value; - - auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status property"), nc_sender_monitor_link_status_property_id, nmos::fields::nc::link_status, U("NcLinkStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status message property"), nc_sender_monitor_link_status_message_property_id, nmos::fields::nc::link_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status transition counter property"), nc_sender_monitor_link_status_transition_counter_property_id, nmos::fields::nc::link_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Transmission status property"), nc_sender_monitor_transmission_status_property_id, nmos::fields::nc::transmission_status, U("NcTransmissionStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Transmission status message property"), nc_sender_monitor_transmission_status_message_property_id, nmos::fields::nc::transmission_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Transmission status transition counter property"), nc_sender_monitor_transmission_status_transition_counter_property_id, nmos::fields::nc::transmission_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status property"), nc_sender_monitor_external_synchronization_status_property_id, nmos::fields::nc::external_synchronization_status, U("NcSynchronizationStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status message property"), nc_sender_monitor_external_synchronization_status_message_property_id, nmos::fields::nc::external_synchronization_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status transition counter property"), nc_sender_monitor_external_synchronization_status_transition_counter_property_id, nmos::fields::nc::external_synchronization_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Synchronization source id property"), nc_sender_monitor_synchronization_source_id_property_id, nmos::fields::nc::synchronization_source_id, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Essence status property"), nc_sender_monitor_essence_status_property_id, nmos::fields::nc::essence_status, U("NcEssenceStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Essence status message property"), nc_sender_monitor_essence_status_message_property_id, nmos::fields::nc::essence_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Essence status property transition counters"), nc_sender_monitor_essence_status_transition_counter_property_id, nmos::fields::nc::essence_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Automatic reset counters and messages property (default: true)"), nc_sender_monitor_auto_reset_monitor_property_id, nmos::fields::nc::auto_reset_monitor, U("NcBoolean"), false, false, false, false, value::null())); - - return properties; - } - web::json::value make_nc_sender_monitor_methods() - { - using web::json::value; + return details::make_nc_class_descriptor(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), make_nc_object_properties(), make_nc_object_methods(), make_nc_object_events()); + } - auto methods = value::array(); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Gets the transmission error counters"), nc_sender_monitor_get_transmission_error_counters_method_id, U("GetTransmissionErrorCounters"), U("NcMethodResultCounters"), value::array(), false)); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Resets ALL counters"), nc_sender_monitor_reset_monitor_method_id, U("ResetCountersAndMessages"), U("NcMethodResult"), value::array(), false)); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.1.html + web::json::value make_nc_block_class() + { + using web::json::value; - return methods; - } - web::json::value make_nc_sender_monitor_events() - { - using web::json::value; + return details::make_nc_class_descriptor(U("NcBlock class descriptor"), nc_block_class_id, U("NcBlock"), make_nc_block_properties(), make_nc_block_methods(), make_nc_block_events()); + } - return value::array(); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.2.html + web::json::value make_nc_worker_class() + { + using web::json::value; - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon - web::json::value make_nc_ident_beacon_properties() - { - using web::json::value; + return details::make_nc_class_descriptor(U("NcWorker class descriptor"), nc_worker_class_id, U("NcWorker"), make_nc_worker_properties(), make_nc_worker_methods(), make_nc_worker_events()); + } - auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Indicator active state"), nc_ident_beacon_active_property_id, nmos::fields::nc::active, U("NcBoolean"), false, false, false, false, value::null())); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.html + web::json::value make_nc_manager_class() + { + using web::json::value; - return properties; - } - web::json::value make_nc_ident_beacon_methods() - { - using web::json::value; + return details::make_nc_class_descriptor(U("NcManager class descriptor"), nc_manager_class_id, U("NcManager"), make_nc_manager_properties(), make_nc_manager_methods(), make_nc_manager_events()); + } - return value::array(); - } - web::json::value make_nc_ident_beacon_events() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.1.html + web::json::value make_nc_device_manager_class() + { + using web::json::value; - return value::array(); - } + return details::make_nc_class_descriptor(U("NcDeviceManager class descriptor"), nc_device_manager_class_id, U("NcDeviceManager"), U("DeviceManager"), make_nc_device_manager_properties(), make_nc_device_manager_methods(), make_nc_device_manager_events()); + } - // Device configuration classes - // NcBulkPropertiesManager - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager - web::json::value make_nc_bulk_properties_manager_properties() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.2.html + web::json::value make_nc_class_manager_class() + { + using web::json::value; - return value::array(); - } - web::json::value make_nc_bulk_properties_manager_methods() - { - using web::json::value; + return details::make_nc_class_descriptor(U("NcClassManager class descriptor"), nc_class_manager_class_id, U("NcClassManager"), U("ClassManager"), make_nc_class_manager_properties(), make_nc_class_manager_methods(), make_nc_class_manager_events()); + } - auto methods = value::array(); + // Identification feature set control classes + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon + web::json::value make_nc_ident_beacon_class() { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The target role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will return properties on specified path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true, property holders returned will contain non-null property descriptors and for full backups the ClassManager role path will also be included"), nmos::fields::nc::include_descriptors, U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get bulk object properties by given path"), nc_bulk_properties_manager_get_properties_by_path_method_id, U("GetPropertiesByPath"), U("NcMethodResultBulkPropertiesHolder"), parameters, false)); + using web::json::value; + + return details::make_nc_class_descriptor(U("NcIdentBeacon class descriptor"), nc_ident_beacon_class_id, U("NcIdentBeacon"), make_nc_ident_beacon_properties(), make_nc_ident_beacon_methods(), make_nc_ident_beacon_events()); } + + // Monitoring feature set control classes + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstatusmonitor + web::json::value make_nc_status_monitor_class() { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkPropertiesHolder"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Defines the restore mode to be applied"), nmos::fields::nc::restore_mode, U("NcRestoreMode"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Validate bulk properties for setting by given paths"), nc_bulk_properties_manager_validate_set_properties_by_path_method_id, U("ValidateSetPropertiesByPath"), U("NcMethodResultObjectPropertiesSetValidation"), parameters, false)); + using web::json::value; + + return details::make_nc_class_descriptor(U("NcStatusMonitor class descriptor"), nc_status_monitor_class_id, U("NcStatusMonitor"), make_nc_status_monitor_properties(), make_nc_status_monitor_methods(), make_nc_status_monitor_events()); } + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor + web::json::value make_nc_receiver_monitor_class() { - auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkPropertiesHolder"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Defines the restore mode to be applied"), nmos::fields::nc::restore_mode, U("NcRestoreMode"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Set bulk properties for setting by given paths"), nc_bulk_properties_manager_set_properties_by_path_method_id, U("SetPropertiesByPath"), U("NcMethodResultObjectPropertiesSetValidation"), parameters, false)); + using web::json::value; + + return details::make_nc_class_descriptor(U("NcReceiverMonitor class descriptor"), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), make_nc_receiver_monitor_properties(), make_nc_receiver_monitor_methods(), make_nc_receiver_monitor_events()); } - return methods; - } - web::json::value make_nc_bulk_properties_manager_events() - { - using web::json::value; + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsendermonitor + web::json::value make_nc_sender_monitor_class() + { + using web::json::value; - return value::array(); - } + return details::make_nc_class_descriptor(U("NcSenderMonitor class descriptor"), nc_sender_monitor_class_id, U("NcSenderMonitor"), make_nc_sender_monitor_properties(), make_nc_sender_monitor_methods(), make_nc_sender_monitor_events()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.html - web::json::value make_nc_object_class() - { - using web::json::value; + // Device configuration feature set control classes + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager + web::json::value make_nc_bulk_properties_manager_class() + { + using web::json::value; - return details::make_nc_class_descriptor(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), make_nc_object_properties(), make_nc_object_methods(), make_nc_object_events()); - } + return details::make_nc_class_descriptor(U("NcBulkPropertiesManager class descriptor"), nc_bulk_properties_manager_class_id, U("NcBulkPropertiesManager"), U("BulkPropertiesManager"), make_nc_bulk_properties_manager_properties(), make_nc_bulk_properties_manager_methods(), make_nc_bulk_properties_manager_events()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.1.html - web::json::value make_nc_block_class() - { - using web::json::value; + // Primitive datatypes + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_boolean_datatype() + { + using web::json::value; - return details::make_nc_class_descriptor(U("NcBlock class descriptor"), nc_block_class_id, U("NcBlock"), make_nc_block_properties(), make_nc_block_methods(), make_nc_block_events()); - } + return details::make_nc_datatype_descriptor_primitive(U("Boolean primitive type"), U("NcBoolean"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.2.html - web::json::value make_nc_worker_class() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_int16_datatype() + { + using web::json::value; - return details::make_nc_class_descriptor(U("NcWorker class descriptor"), nc_worker_class_id, U("NcWorker"), make_nc_worker_properties(), make_nc_worker_methods(), make_nc_worker_events()); - } + return details::make_nc_datatype_descriptor_primitive(U("short"), U("NcInt16"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.html - web::json::value make_nc_manager_class() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_int32_datatype() + { + using web::json::value; - return details::make_nc_class_descriptor(U("NcManager class descriptor"), nc_manager_class_id, U("NcManager"), make_nc_manager_properties(), make_nc_manager_methods(), make_nc_manager_events()); - } + return details::make_nc_datatype_descriptor_primitive(U("long"), U("NcInt32"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.1.html - web::json::value make_nc_device_manager_class() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_int64_datatype() + { + using web::json::value; - return details::make_nc_class_descriptor(U("NcDeviceManager class descriptor"), nc_device_manager_class_id, U("NcDeviceManager"), U("DeviceManager"), make_nc_device_manager_properties(), make_nc_device_manager_methods(), make_nc_device_manager_events()); - } + return details::make_nc_datatype_descriptor_primitive(U("longlong"), U("NcInt64"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.2.html - web::json::value make_nc_class_manager_class() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_uint16_datatype() + { + using web::json::value; - return details::make_nc_class_descriptor(U("NcClassManager class descriptor"), nc_class_manager_class_id, U("NcClassManager"), U("ClassManager"), make_nc_class_manager_properties(), make_nc_class_manager_methods(), make_nc_class_manager_events()); - } + return details::make_nc_datatype_descriptor_primitive(U("unsignedshort"), U("NcUint16"), value::null()); + } - // Identification feature set control classes - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon - web::json::value make_nc_ident_beacon_class() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_uint32_datatype() + { + using web::json::value; - return details::make_nc_class_descriptor(U("NcIdentBeacon class descriptor"), nc_ident_beacon_class_id, U("NcIdentBeacon"), make_nc_ident_beacon_properties(), make_nc_ident_beacon_methods(), make_nc_ident_beacon_events()); - } + return details::make_nc_datatype_descriptor_primitive(U("unsignedlong"), U("NcUint32"), value::null()); + } - // Monitoring feature set control classes - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstatusmonitor - web::json::value make_nc_status_monitor_class() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_uint64_datatype() + { + using web::json::value; - return details::make_nc_class_descriptor(U("NcStatusMonitor class descriptor"), nc_status_monitor_class_id, U("NcStatusMonitor"), make_nc_status_monitor_properties(), make_nc_status_monitor_methods(), make_nc_status_monitor_events()); - } + return details::make_nc_datatype_descriptor_primitive(U("unsignedlonglong"), U("NcUint64"), value::null()); + } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor - web::json::value make_nc_receiver_monitor_class() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_float32_datatype() + { + using web::json::value; - return details::make_nc_class_descriptor(U("NcReceiverMonitor class descriptor"), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), make_nc_receiver_monitor_properties(), make_nc_receiver_monitor_methods(), make_nc_receiver_monitor_events()); - } + return details::make_nc_datatype_descriptor_primitive(U("unrestrictedfloat"), U("NcFloat32"), value::null()); + } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsendermonitor - web::json::value make_nc_sender_monitor_class() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_float64_datatype() + { + using web::json::value; - return details::make_nc_class_descriptor(U("NcSenderMonitor class descriptor"), nc_sender_monitor_class_id, U("NcSenderMonitor"), make_nc_sender_monitor_properties(), make_nc_sender_monitor_methods(), make_nc_sender_monitor_events()); - } + return details::make_nc_datatype_descriptor_primitive(U("unrestricteddouble"), U("NcFloat64"), value::null()); + } - // Device configuration feature set control classes - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager - web::json::value make_nc_bulk_properties_manager_class() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_string_datatype() + { + using web::json::value; - return details::make_nc_class_descriptor(U("NcBulkPropertiesManager class descriptor"), nc_bulk_properties_manager_class_id, U("NcBulkPropertiesManager"), U("BulkPropertiesManager"), make_nc_bulk_properties_manager_properties(), make_nc_bulk_properties_manager_methods(), make_nc_bulk_properties_manager_events()); - } + return details::make_nc_datatype_descriptor_primitive(U("UTF-8 string"), U("NcString"), value::null()); + } - // Primitive datatypes - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_boolean_datatype() - { - using web::json::value; - return details::make_nc_datatype_descriptor_primitive(U("Boolean primitive type"), U("NcBoolean"), value::null()); - } + // Standard datatypes + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcBlockMemberDescriptor.html + web::json::value make_nc_block_member_descriptor_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_int16_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Role of member in its containing block"), nmos::fields::nc::role, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("OID of member"), nmos::fields::nc::oid, U("NcOid"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff member's OID is hardwired into device"), nmos::fields::nc::constant_oid, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Class ID"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("User label"), nmos::fields::nc::user_label, U("NcString"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Containing block's OID"), nmos::fields::nc::owner, U("NcOid"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Descriptor which is specific to a block member"), U("NcBlockMemberDescriptor"), fields, U("NcDescriptor"), value::null()); + } - return details::make_nc_datatype_descriptor_primitive(U("short"), U("NcInt16"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassDescriptor.html + web::json::value make_nc_class_descriptor_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_int32_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Identity of the class"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of the class"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Role if the class has fixed role (manager classes)"), nmos::fields::nc::fixed_role, U("NcString"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property descriptors"), nmos::fields::nc::properties, U("NcPropertyDescriptor"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Method descriptors"), nmos::fields::nc::methods, U("NcMethodDescriptor"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Event descriptors"), nmos::fields::nc::events, U("NcEventDescriptor"), false, true, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Descriptor of a class"), U("NcClassDescriptor"), fields, U("NcDescriptor"), value::null()); + } - return details::make_nc_datatype_descriptor_primitive(U("long"), U("NcInt32"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassId.html + web::json::value make_nc_class_id_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_int64_datatype() - { - using web::json::value; + return details::make_nc_datatype_typedef(U("Sequence of class ID fields"), U("NcClassId"), true, U("NcInt32"), value::null()); + } - return details::make_nc_datatype_descriptor_primitive(U("longlong"), U("NcInt64"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptor.html + web::json::value make_nc_datatype_descriptor_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_uint16_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Datatype name"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Type: Primitive, Typedef, Struct, Enum"), nmos::fields::nc::type, U("NcDatatypeType"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Base datatype descriptor"), U("NcDatatypeDescriptor"), fields, U("NcDescriptor"), value::null()); + } - return details::make_nc_datatype_descriptor_primitive(U("unsignedshort"), U("NcUint16"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorEnum.html + web::json::value make_nc_datatype_descriptor_enum_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_uint32_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("One item descriptor per enum option"), nmos::fields::nc::items, U("NcEnumItemDescriptor"), false, true, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Enum datatype descriptor"), U("NcDatatypeDescriptorEnum"), fields, U("NcDatatypeDescriptor"), value::null()); + } - return details::make_nc_datatype_descriptor_primitive(U("unsignedlong"), U("NcUint32"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorPrimitive.html + web::json::value make_nc_datatype_descriptor_primitive_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_uint64_datatype() - { - using web::json::value; + auto fields = value::array(); + return details::make_nc_datatype_descriptor_struct(U("Primitive datatype descriptor"), U("NcDatatypeDescriptorPrimitive"), fields, U("NcDatatypeDescriptor"), value::null()); + } - return details::make_nc_datatype_descriptor_primitive(U("unsignedlonglong"), U("NcUint64"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorStruct.html + web::json::value make_nc_datatype_descriptor_struct_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_float32_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("One item descriptor per field of the struct"), nmos::fields::nc::fields, U("NcFieldDescriptor"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of the parent type if any or null if it has no parent"), nmos::fields::nc::parent_type, U("NcName"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Struct datatype descriptor"), U("NcDatatypeDescriptorStruct"), fields, U("NcDatatypeDescriptor"), value::null()); + } - return details::make_nc_datatype_descriptor_primitive(U("unrestrictedfloat"), U("NcFloat32"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorTypeDef.html + web::json::value make_nc_datatype_descriptor_type_def_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_float64_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Original typedef datatype name"), nmos::fields::nc::parent_type, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff type is a typedef sequence of another type"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Type def datatype descriptor"), U("NcDatatypeDescriptorTypeDef"), fields, U("NcDatatypeDescriptor"), value::null()); + } - return details::make_nc_datatype_descriptor_primitive(U("unrestricteddouble"), U("NcFloat64"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeType.html + web::json::value make_nc_datatype_type_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_string_datatype() - { - using web::json::value; + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Primitive datatype"), U("Primitive"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Simple alias of another datatype"), U("Typedef"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Data structure"), U("Struct"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Enum datatype"), U("Enum"), 3)); + return details::make_nc_datatype_descriptor_enum(U("Datatype type"), U("NcDatatypeType"), items, value::null()); + } - return details::make_nc_datatype_descriptor_primitive(U("UTF-8 string"), U("NcString"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDescriptor.html + web::json::value make_nc_descriptor_datatype() + { + using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional user facing description"), nmos::fields::nc::description, U("NcString"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Base descriptor"), U("NcDescriptor"), fields, value::null()); + } - // Standard datatypes - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcBlockMemberDescriptor.html - web::json::value make_nc_block_member_descriptor_datatype() - { - using web::json::value; - - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Role of member in its containing block"), nmos::fields::nc::role, U("NcString"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("OID of member"), nmos::fields::nc::oid, U("NcOid"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff member's OID is hardwired into device"), nmos::fields::nc::constant_oid, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Class ID"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("User label"), nmos::fields::nc::user_label, U("NcString"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Containing block's OID"), nmos::fields::nc::owner, U("NcOid"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Descriptor which is specific to a block member"), U("NcBlockMemberDescriptor"), fields, U("NcDescriptor"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceGenericState.html + web::json::value make_nc_device_generic_state_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassDescriptor.html - web::json::value make_nc_class_descriptor_datatype() - { - using web::json::value; - - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Identity of the class"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of the class"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Role if the class has fixed role (manager classes)"), nmos::fields::nc::fixed_role, U("NcString"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property descriptors"), nmos::fields::nc::properties, U("NcPropertyDescriptor"), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Method descriptors"), nmos::fields::nc::methods, U("NcMethodDescriptor"), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Event descriptors"), nmos::fields::nc::events, U("NcEventDescriptor"), false, true, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Descriptor of a class"), U("NcClassDescriptor"), fields, U("NcDescriptor"), value::null()); - } + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Unknown"), U("Unknown"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Normal operation"), U("NormalOperation"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Device is initializing"), U("Initializing"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Device is performing a software or firmware update"), U("Updating"), 3)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Device is experiencing a licensing error"), U("LicensingError"), 4)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Device is experiencing an internal error"), U("InternalError"), 5)); + return details::make_nc_datatype_descriptor_enum(U("Device generic operational state"), U("NcDeviceGenericState"), items, value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassId.html - web::json::value make_nc_class_id_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceOperationalState.html + web::json::value make_nc_device_operational_state_datatype() + { + using web::json::value; - return details::make_nc_datatype_typedef(U("Sequence of class ID fields"), U("NcClassId"), true, U("NcInt32"), value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Generic operational state"), nmos::fields::nc::generic_state, U("NcDeviceGenericState"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Specific device details"), nmos::fields::nc::device_specific_details, U("NcString"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Device operational state"), U("NcDeviceOperationalState"), fields, value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptor.html - web::json::value make_nc_datatype_descriptor_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcElementId.html + web::json::value make_nc_element_id_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Datatype name"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Type: Primitive, Typedef, Struct, Enum"), nmos::fields::nc::type, U("NcDatatypeType"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Base datatype descriptor"), U("NcDatatypeDescriptor"), fields, U("NcDescriptor"), value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Level of the element"), nmos::fields::nc::level, U("NcUint16"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Index of the element"), nmos::fields::nc::index, U("NcUint16"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Class element id which contains the level and index"), U("NcElementId"), fields, value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorEnum.html - web::json::value make_nc_datatype_descriptor_enum_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEnumItemDescriptor.html + web::json::value make_nc_enum_item_descriptor_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("One item descriptor per enum option"), nmos::fields::nc::items, U("NcEnumItemDescriptor"), false, true, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Enum datatype descriptor"), U("NcDatatypeDescriptorEnum"), fields, U("NcDatatypeDescriptor"), value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of option"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Enum item numerical value"), nmos::fields::nc::value, U("NcUint16"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Descriptor of an enum item"), U("NcEnumItemDescriptor"), fields, U("NcDescriptor"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorPrimitive.html - web::json::value make_nc_datatype_descriptor_primitive_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventDescriptor.html + web::json::value make_nc_event_descriptor_datatype() + { + using web::json::value; - auto fields = value::array(); - return details::make_nc_datatype_descriptor_struct(U("Primitive datatype descriptor"), U("NcDatatypeDescriptorPrimitive"), fields, U("NcDatatypeDescriptor"), value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Event id with level and index"), nmos::fields::nc::id, U("NcEventId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of event"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of event data's datatype"), nmos::fields::nc::event_datatype, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is marked as deprecated"), nmos::fields::nc::is_deprecated, U("NcBoolean"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Descriptor of a class event"), U("NcEventDescriptor"), fields, U("NcDescriptor"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorStruct.html - web::json::value make_nc_datatype_descriptor_struct_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventId.html + web::json::value make_nc_event_id_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("One item descriptor per field of the struct"), nmos::fields::nc::fields, U("NcFieldDescriptor"), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of the parent type if any or null if it has no parent"), nmos::fields::nc::parent_type, U("NcName"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Struct datatype descriptor"), U("NcDatatypeDescriptorStruct"), fields, U("NcDatatypeDescriptor"), value::null()); - } + return details::make_nc_datatype_descriptor_struct(U("Event id which contains the level and index"), U("NcEventId"), value::array(), U("NcElementId"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorTypeDef.html - web::json::value make_nc_datatype_descriptor_type_def_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcFieldDescriptor.html + web::json::value make_nc_field_descriptor_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Original typedef datatype name"), nmos::fields::nc::parent_type, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff type is a typedef sequence of another type"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Type def datatype descriptor"), U("NcDatatypeDescriptorTypeDef"), fields, U("NcDatatypeDescriptor"), value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of field"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of field's datatype. Can only ever be null if the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff field is nullable"), nmos::fields::nc::is_nullable, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff field is a sequence"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Descriptor of a field of a struct"), U("NcFieldDescriptor"), fields, U("NcDescriptor"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeType.html - web::json::value make_nc_datatype_type_datatype() - { - using web::json::value; - - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Primitive datatype"), U("Primitive"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Simple alias of another datatype"), U("Typedef"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Data structure"), U("Struct"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Enum datatype"), U("Enum"), 3)); - return details::make_nc_datatype_descriptor_enum(U("Datatype type"), U("NcDatatypeType"), items, value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcId.html + web::json::value make_nc_id_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDescriptor.html - web::json::value make_nc_descriptor_datatype() - { - using web::json::value; + return details::make_nc_datatype_typedef(U("Identity handler"), U("NcId"), false, U("NcUint32"), value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional user facing description"), nmos::fields::nc::description, U("NcString"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Base descriptor"), U("NcDescriptor"), fields, value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcManufacturer.html + web::json::value make_nc_manufacturer_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceGenericState.html - web::json::value make_nc_device_generic_state_datatype() - { - using web::json::value; - - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Unknown"), U("Unknown"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Normal operation"), U("NormalOperation"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Device is initializing"), U("Initializing"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Device is performing a software or firmware update"), U("Updating"), 3)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Device is experiencing a licensing error"), U("LicensingError"), 4)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Device is experiencing an internal error"), U("InternalError"), 5)); - return details::make_nc_datatype_descriptor_enum(U("Device generic operational state"), U("NcDeviceGenericState"), items, value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Manufacturer's name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("IEEE OUI or CID of manufacturer"), nmos::fields::nc::organization_id, U("NcOrganizationId"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("URL of the manufacturer's website"), nmos::fields::nc::website, U("NcUri"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Manufacturer descriptor"), U("NcManufacturer"), fields, value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceOperationalState.html - web::json::value make_nc_device_operational_state_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodDescriptor.html + web::json::value make_nc_method_descriptor_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Generic operational state"), nmos::fields::nc::generic_state, U("NcDeviceGenericState"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Specific device details"), nmos::fields::nc::device_specific_details, U("NcString"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Device operational state"), U("NcDeviceOperationalState"), fields, value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Method id with level and index"), nmos::fields::nc::id, U("NcMethodId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of method"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of method result's datatype"), nmos::fields::nc::result_datatype, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Parameter descriptors if any"), nmos::fields::nc::parameters, U("NcParameterDescriptor"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is marked as deprecated"), nmos::fields::nc::is_deprecated, U("NcBoolean"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Descriptor of a class method"), U("NcMethodDescriptor"), fields, U("NcDescriptor"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcElementId.html - web::json::value make_nc_element_id_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodId.html + web::json::value make_nc_method_id_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Level of the element"), nmos::fields::nc::level, U("NcUint16"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Index of the element"), nmos::fields::nc::index, U("NcUint16"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Class element id which contains the level and index"), U("NcElementId"), fields, value::null()); - } + return details::make_nc_datatype_descriptor_struct(U("Method id which contains the level and index"), U("NcMethodId"), value::array(), U("NcElementId"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEnumItemDescriptor.html - web::json::value make_nc_enum_item_descriptor_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResult.html + web::json::value make_nc_method_result_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of option"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Enum item numerical value"), nmos::fields::nc::value, U("NcUint16"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Descriptor of an enum item"), U("NcEnumItemDescriptor"), fields, U("NcDescriptor"), value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Status for the invoked method"), nmos::fields::nc::status, U("NcMethodStatus"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Base result of the invoked method"), U("NcMethodResult"), fields, value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventDescriptor.html - web::json::value make_nc_event_descriptor_datatype() - { - using web::json::value; - - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Event id with level and index"), nmos::fields::nc::id, U("NcEventId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of event"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of event data's datatype"), nmos::fields::nc::event_datatype, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is marked as deprecated"), nmos::fields::nc::is_deprecated, U("NcBoolean"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Descriptor of a class event"), U("NcEventDescriptor"), fields, U("NcDescriptor"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultBlockMemberDescriptors.html + web::json::value make_nc_method_result_block_member_descriptors_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventId.html - web::json::value make_nc_event_id_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Block member descriptors method result value"), nmos::fields::nc::value, U("NcBlockMemberDescriptor"), false, true, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Method result containing block member descriptors as the value"), U("NcMethodResultBlockMemberDescriptors"), fields, U("NcMethodResult"), value::null()); + } - return details::make_nc_datatype_descriptor_struct(U("Event id which contains the level and index"), U("NcEventId"), value::array(), U("NcElementId"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultClassDescriptor.html + web::json::value make_nc_method_result_class_descriptor_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcFieldDescriptor.html - web::json::value make_nc_field_descriptor_datatype() - { - using web::json::value; - - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of field"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of field's datatype. Can only ever be null if the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff field is nullable"), nmos::fields::nc::is_nullable, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff field is a sequence"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Descriptor of a field of a struct"), U("NcFieldDescriptor"), fields, U("NcDescriptor"), value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Class descriptor method result value"), nmos::fields::nc::value, U("NcClassDescriptor"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Method result containing a class descriptor as the value"), U("NcMethodResultClassDescriptor"), fields, U("NcMethodResult"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcId.html - web::json::value make_nc_id_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultDatatypeDescriptor.html + web::json::value make_nc_method_result_datatype_descriptor_datatype() + { + using web::json::value; - return details::make_nc_datatype_typedef(U("Identity handler"), U("NcId"), false, U("NcUint32"), value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Datatype descriptor method result value"), nmos::fields::nc::value, U("NcDatatypeDescriptor"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Method result containing a datatype descriptor as the value"), U("NcMethodResultDatatypeDescriptor"), fields, U("NcMethodResult"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcManufacturer.html - web::json::value make_nc_manufacturer_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultError.html + web::json::value make_nc_method_result_error_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Manufacturer's name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("IEEE OUI or CID of manufacturer"), nmos::fields::nc::organization_id, U("NcOrganizationId"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("URL of the manufacturer's website"), nmos::fields::nc::website, U("NcUri"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Manufacturer descriptor"), U("NcManufacturer"), fields, value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Error message"), nmos::fields::nc::error_message, U("NcString"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Error result - to be used when the method call encounters an error"), U("NcMethodResultError"), fields, U("NcMethodResult"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodDescriptor.html - web::json::value make_nc_method_descriptor_datatype() - { - using web::json::value; - - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Method id with level and index"), nmos::fields::nc::id, U("NcMethodId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of method"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of method result's datatype"), nmos::fields::nc::result_datatype, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Parameter descriptors if any"), nmos::fields::nc::parameters, U("NcParameterDescriptor"), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is marked as deprecated"), nmos::fields::nc::is_deprecated, U("NcBoolean"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Descriptor of a class method"), U("NcMethodDescriptor"), fields, U("NcDescriptor"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultId.html + web::json::value make_nc_method_result_id_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodId.html - web::json::value make_nc_method_id_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Id result value"), nmos::fields::nc::value, U("NcId"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Id method result"), U("NcMethodResultId"), fields, U("NcMethodResult"), value::null()); + } - return details::make_nc_datatype_descriptor_struct(U("Method id which contains the level and index"), U("NcMethodId"), value::array(), U("NcElementId"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultLength.html + web::json::value make_nc_method_result_length_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResult.html - web::json::value make_nc_method_result_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Length result value"), nmos::fields::nc::value, U("NcUint32"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Length method result"), U("NcMethodResultLength"), fields, U("NcMethodResult"), value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Status for the invoked method"), nmos::fields::nc::status, U("NcMethodStatus"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Base result of the invoked method"), U("NcMethodResult"), fields, value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultPropertyValue.html + web::json::value make_nc_method_result_property_value_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultBlockMemberDescriptors.html - web::json::value make_nc_method_result_block_member_descriptors_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Getter method value for the associated property"), nmos::fields::nc::value, true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Result when invoking the getter method associated with a property"), U("NcMethodResultPropertyValue"), fields, U("NcMethodResult"), value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Block member descriptors method result value"), nmos::fields::nc::value, U("NcBlockMemberDescriptor"), false, true, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Method result containing block member descriptors as the value"), U("NcMethodResultBlockMemberDescriptors"), fields, U("NcMethodResult"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodStatus.html + web::json::value make_nc_method_status_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultClassDescriptor.html - web::json::value make_nc_method_result_class_descriptor_datatype() - { - using web::json::value; + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call was successful"), U("Ok"), 200)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call was successful but targeted property is deprecated"), U("PropertyDeprecated"), 298)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call was successful but method is deprecated"), U("MethodDeprecated"), 299)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Badly-formed command (e.g. the incoming command has invalid message encoding and cannot be parsed by the underlying protocol)"), U("BadCommandFormat"), 400)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Client is not authorized"), U("Unauthorized"), 401)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Command addresses a nonexistent object"), U("BadOid"), 404)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Attempt to change read-only state"), U("Readonly"), 405)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call is invalid in current operating context (e.g. attempting to invoke a method when the object is disabled)"), U("InvalidRequest"), 406)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("There is a conflict with the current state of the device"), U("Conflict"), 409)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Something was too big"), U("BufferOverflow"), 413)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Index is outside the available range"), U("IndexOutOfBounds"), 414)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method parameter does not meet expectations (e.g. attempting to invoke a method with an invalid type for one of its parameters)"), U("ParameterError"), 417)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Addressed object is locked"), U("Locked"), 423)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Internal device error"), U("DeviceError"), 500)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Addressed method is not implemented by the addressed object"), U("MethodNotImplemented"), 501)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Addressed property is not implemented by the addressed object"), U("PropertyNotImplemented"), 502)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("The device is not ready to handle any commands"), U("NotReady"), 503)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call did not finish within the allotted time"), U("Timeout"), 504)); + return details::make_nc_datatype_descriptor_enum(U("Method invokation status"), U("NcMethodStatus"), items, value::null()); + } + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcName.html + web::json::value make_nc_name_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Class descriptor method result value"), nmos::fields::nc::value, U("NcClassDescriptor"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Method result containing a class descriptor as the value"), U("NcMethodResultClassDescriptor"), fields, U("NcMethodResult"), value::null()); - } + return details::make_nc_datatype_typedef(U("Programmatically significant name, alphanumerics + underscore, no spaces"), U("NcName"), false, U("NcString"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultDatatypeDescriptor.html - web::json::value make_nc_method_result_datatype_descriptor_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOid.html + web::json::value make_nc_oid_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Datatype descriptor method result value"), nmos::fields::nc::value, U("NcDatatypeDescriptor"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Method result containing a datatype descriptor as the value"), U("NcMethodResultDatatypeDescriptor"), fields, U("NcMethodResult"), value::null()); - } + return details::make_nc_datatype_typedef(U("Object id"), U("NcOid"), false, U("NcUint32"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultError.html - web::json::value make_nc_method_result_error_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOrganizationId.html + web::json::value make_nc_organization_id_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Error message"), nmos::fields::nc::error_message, U("NcString"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Error result - to be used when the method call encounters an error"), U("NcMethodResultError"), fields, U("NcMethodResult"), value::null()); - } + return details::make_nc_datatype_typedef(U("Unique 24-bit organization id"), U("NcOrganizationId"), false, U("NcInt32"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultId.html - web::json::value make_nc_method_result_id_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraints.html + web::json::value make_nc_parameter_constraints_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Id result value"), nmos::fields::nc::value, U("NcId"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Id method result"), U("NcMethodResultId"), fields, U("NcMethodResult"), value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Default value"), nmos::fields::nc::default_value, true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Abstract parameter constraints class"), U("NcParameterConstraints"), fields, value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultLength.html - web::json::value make_nc_method_result_length_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsNumber.html + web::json::value make_nc_parameter_constraints_number_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Length result value"), nmos::fields::nc::value, U("NcUint32"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Length method result"), U("NcMethodResultLength"), fields, U("NcMethodResult"), value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional minimum"), nmos::fields::nc::minimum, true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional maximum"), nmos::fields::nc::maximum, true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional step"), nmos::fields::nc::step, true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Number parameter constraints class"), U("NcParameterConstraintsNumber"), fields, U("NcParameterConstraints"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultPropertyValue.html - web::json::value make_nc_method_result_property_value_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsString.html + web::json::value make_nc_parameter_constraints_string_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Getter method value for the associated property"), nmos::fields::nc::value, true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Result when invoking the getter method associated with a property"), U("NcMethodResultPropertyValue"), fields, U("NcMethodResult"), value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Maximum characters allowed"), nmos::fields::nc::max_characters, U("NcUint32"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Regex pattern"), nmos::fields::nc::pattern, U("NcRegex"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("String parameter constraints class"), U("NcParameterConstraintsString"), fields, U("NcParameterConstraints"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodStatus.html - web::json::value make_nc_method_status_datatype() - { - using web::json::value; - - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call was successful"), U("Ok"), 200)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call was successful but targeted property is deprecated"), U("PropertyDeprecated"), 298)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call was successful but method is deprecated"), U("MethodDeprecated"), 299)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Badly-formed command (e.g. the incoming command has invalid message encoding and cannot be parsed by the underlying protocol)"), U("BadCommandFormat"), 400)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Client is not authorized"), U("Unauthorized"), 401)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Command addresses a nonexistent object"), U("BadOid"), 404)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Attempt to change read-only state"), U("Readonly"), 405)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call is invalid in current operating context (e.g. attempting to invoke a method when the object is disabled)"), U("InvalidRequest"), 406)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("There is a conflict with the current state of the device"), U("Conflict"), 409)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Something was too big"), U("BufferOverflow"), 413)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Index is outside the available range"), U("IndexOutOfBounds"), 414)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method parameter does not meet expectations (e.g. attempting to invoke a method with an invalid type for one of its parameters)"), U("ParameterError"), 417)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Addressed object is locked"), U("Locked"), 423)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Internal device error"), U("DeviceError"), 500)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Addressed method is not implemented by the addressed object"), U("MethodNotImplemented"), 501)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Addressed property is not implemented by the addressed object"), U("PropertyNotImplemented"), 502)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("The device is not ready to handle any commands"), U("NotReady"), 503)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call did not finish within the allotted time"), U("Timeout"), 504)); - return details::make_nc_datatype_descriptor_enum(U("Method invokation status"), U("NcMethodStatus"), items, value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterDescriptor.html + web::json::value make_nc_parameter_descriptor_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcName.html - web::json::value make_nc_name_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of parameter"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of parameter's datatype. Can only ever be null if the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is nullable"), nmos::fields::nc::is_nullable, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is a sequence"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Descriptor of a method parameter"), U("NcParameterDescriptor"), fields, U("NcDescriptor"), value::null()); + } - return details::make_nc_datatype_typedef(U("Programmatically significant name, alphanumerics + underscore, no spaces"), U("NcName"), false, U("NcString"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcProduct.html + web::json::value make_nc_product_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOid.html - web::json::value make_nc_oid_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Product name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Manufacturer's unique key to product - model number, SKU, etc"), nmos::fields::nc::key, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Manufacturer's product revision level code"), nmos::fields::nc::revision_level, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Brand name under which product is sold"), nmos::fields::nc::brand_name, U("NcString"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Unique UUID of product (not product instance)"), nmos::fields::nc::uuid, U("NcUuid"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Text description of product"), nmos::fields::nc::description, U("NcString"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Product descriptor"), U("NcProduct"), fields, value::null()); + } - return details::make_nc_datatype_typedef(U("Object id"), U("NcOid"), false, U("NcUint32"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangeType.html + web::json::value make_nc_property_change_type_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOrganizationId.html - web::json::value make_nc_organization_id_datatype() - { - using web::json::value; + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Current value changed"), U("ValueChanged"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Sequence item added"), U("SequenceItemAdded"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Sequence item changed"), U("SequenceItemChanged"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Sequence item removed"), U("SequenceItemRemoved"), 3)); + return details::make_nc_datatype_descriptor_enum(U("Type of property change"), U("NcPropertyChangeType"), items, value::null()); + } - return details::make_nc_datatype_typedef(U("Unique 24-bit organization id"), U("NcOrganizationId"), false, U("NcInt32"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangedEventData.html + web::json::value make_nc_property_changed_event_data_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraints.html - web::json::value make_nc_parameter_constraints_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("The id of the property that changed"), nmos::fields::nc::property_id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Information regarding the change type"), nmos::fields::nc::change_type, U("NcPropertyChangeType"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property-type specific value"), nmos::fields::nc::value, true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Index of sequence item if the property is a sequence"), nmos::fields::nc::sequence_item_index,U("NcId"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Payload of property-changed event"), U("NcPropertyChangedEventData"), fields, value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Default value"), nmos::fields::nc::default_value, true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Abstract parameter constraints class"), U("NcParameterConstraints"), fields, value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraints.html + web::json::value make_nc_property_contraints_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsNumber.html - web::json::value make_nc_parameter_constraints_number_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("The id of the property being constrained"), nmos::fields::nc::property_id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional default value"), nmos::fields::nc::default_value, true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Property constraints class"), U("NcPropertyConstraints"), fields, value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional minimum"), nmos::fields::nc::minimum, true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional maximum"), nmos::fields::nc::maximum, true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional step"), nmos::fields::nc::step, true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Number parameter constraints class"), U("NcParameterConstraintsNumber"), fields, U("NcParameterConstraints"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsNumber.html + web::json::value make_nc_property_constraints_number_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsString.html - web::json::value make_nc_parameter_constraints_string_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional minimum"), nmos::fields::nc::minimum, true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional maximum"), nmos::fields::nc::maximum, true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional step"), nmos::fields::nc::step, true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Number property constraints class"), U("NcPropertyConstraintsNumber"), fields, U("NcPropertyConstraints"), value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Maximum characters allowed"), nmos::fields::nc::max_characters, U("NcUint32"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Regex pattern"), nmos::fields::nc::pattern, U("NcRegex"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("String parameter constraints class"), U("NcParameterConstraintsString"), fields, U("NcParameterConstraints"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsString.html + web::json::value make_nc_property_constraints_string_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterDescriptor.html - web::json::value make_nc_parameter_descriptor_datatype() - { - using web::json::value; - - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of parameter"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of parameter's datatype. Can only ever be null if the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is nullable"), nmos::fields::nc::is_nullable, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is a sequence"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Descriptor of a method parameter"), U("NcParameterDescriptor"), fields, U("NcDescriptor"), value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Maximum characters allowed"), nmos::fields::nc::max_characters, U("NcUint32"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Regex pattern"), nmos::fields::nc::pattern, U("NcRegex"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("String property constraints class"), U("NcPropertyConstraintsString"), fields, U("NcPropertyConstraints"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcProduct.html - web::json::value make_nc_product_datatype() - { - using web::json::value; - - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Product name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Manufacturer's unique key to product - model number, SKU, etc"), nmos::fields::nc::key, U("NcString"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Manufacturer's product revision level code"), nmos::fields::nc::revision_level, U("NcString"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Brand name under which product is sold"), nmos::fields::nc::brand_name, U("NcString"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Unique UUID of product (not product instance)"), nmos::fields::nc::uuid, U("NcUuid"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Text description of product"), nmos::fields::nc::description, U("NcString"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Product descriptor"), U("NcProduct"), fields, value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyDescriptor.html + web::json::value make_nc_property_descriptor_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangeType.html - web::json::value make_nc_property_change_type_datatype() - { - using web::json::value; - - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Current value changed"), U("ValueChanged"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Sequence item added"), U("SequenceItemAdded"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Sequence item changed"), U("SequenceItemChanged"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Sequence item removed"), U("SequenceItemRemoved"), 3)); - return details::make_nc_datatype_descriptor_enum(U("Type of property change"), U("NcPropertyChangeType"), items, value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property id with level and index"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of property"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of property's datatype. Can only ever be null if the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is read-only"), nmos::fields::nc::is_read_only, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is nullable"), nmos::fields::nc::is_nullable, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is a sequence"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is marked as deprecated"), nmos::fields::nc::is_deprecated, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Descriptor of a class property"), U("NcPropertyDescriptor"), fields, U("NcDescriptor"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangedEventData.html - web::json::value make_nc_property_changed_event_data_datatype() - { - using web::json::value; - - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("The id of the property that changed"), nmos::fields::nc::property_id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Information regarding the change type"), nmos::fields::nc::change_type, U("NcPropertyChangeType"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property-type specific value"), nmos::fields::nc::value, true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Index of sequence item if the property is a sequence"), nmos::fields::nc::sequence_item_index,U("NcId"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Payload of property-changed event"), U("NcPropertyChangedEventData"), fields, value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyId.html + web::json::value make_nc_property_id_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraints.html - web::json::value make_nc_property_contraints_datatype() - { - using web::json::value; + return details::make_nc_datatype_descriptor_struct(U("Property id which contains the level and index"), U("NcPropertyId"), value::array(), U("NcElementId"), value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("The id of the property being constrained"), nmos::fields::nc::property_id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional default value"), nmos::fields::nc::default_value, true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Property constraints class"), U("NcPropertyConstraints"), fields, value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRegex.html + web::json::value make_nc_regex_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsNumber.html - web::json::value make_nc_property_constraints_number_datatype() - { - using web::json::value; + return details::make_nc_datatype_typedef(U("Regex pattern"), U("NcRegex"), false, U("NcString"), value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional minimum"), nmos::fields::nc::minimum, true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional maximum"), nmos::fields::nc::maximum, true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional step"), nmos::fields::nc::step, true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Number property constraints class"), U("NcPropertyConstraintsNumber"), fields, U("NcPropertyConstraints"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcResetCause.html + web::json::value make_nc_reset_cause_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsString.html - web::json::value make_nc_property_constraints_string_datatype() - { - using web::json::value; + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Unknown"), U("Unknown"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Power on"), U("PowerOn"), 1)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Internal error"), U("InternalError"), 2)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Upgrade"), U("Upgrade"), 3)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Controller request"), U("ControllerRequest"), 4)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Manual request from the front panel"), U("ManualReset"), 5)); + return details::make_nc_datatype_descriptor_enum(U("Reset cause enum"), U("NcResetCause"), items, value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Maximum characters allowed"), nmos::fields::nc::max_characters, U("NcUint32"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Regex pattern"), nmos::fields::nc::pattern, U("NcRegex"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("String property constraints class"), U("NcPropertyConstraintsString"), fields, U("NcPropertyConstraints"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRolePath.html + web::json::value make_nc_role_path_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyDescriptor.html - web::json::value make_nc_property_descriptor_datatype() - { - using web::json::value; - - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property id with level and index"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of property"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of property's datatype. Can only ever be null if the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is read-only"), nmos::fields::nc::is_read_only, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is nullable"), nmos::fields::nc::is_nullable, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is a sequence"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is marked as deprecated"), nmos::fields::nc::is_deprecated, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Descriptor of a class property"), U("NcPropertyDescriptor"), fields, U("NcDescriptor"), value::null()); - } + return details::make_nc_datatype_typedef(U("Role path"), U("NcRolePath"), true, U("NcString"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyId.html - web::json::value make_nc_property_id_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTimeInterval.html + web::json::value make_nc_time_interval_datatype() + { + using web::json::value; - return details::make_nc_datatype_descriptor_struct(U("Property id which contains the level and index"), U("NcPropertyId"), value::array(), U("NcElementId"), value::null()); - } + return details::make_nc_datatype_typedef(U("Time interval described in nanoseconds"), U("NcTimeInterval"), false, U("NcInt64"), value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRegex.html - web::json::value make_nc_regex_datatype() - { - using web::json::value; + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpoint.html + web::json::value make_nc_touchpoint_datatype() + { + using web::json::value; - return details::make_nc_datatype_typedef(U("Regex pattern"), U("NcRegex"), false, U("NcString"), value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Context namespace"), nmos::fields::nc::context_namespace, U("NcString"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Base touchpoint class"), U("NcTouchpoint"), fields, value::null()); + } - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcResetCause.html - web::json::value make_nc_reset_cause_datatype() - { - using web::json::value; - - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Unknown"), U("Unknown"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Power on"), U("PowerOn"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Internal error"), U("InternalError"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Upgrade"), U("Upgrade"), 3)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Controller request"), U("ControllerRequest"), 4)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Manual request from the front panel"), U("ManualReset"), 5)); - return details::make_nc_datatype_descriptor_enum(U("Reset cause enum"), U("NcResetCause"), items, value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmos.html + web::json::value make_nc_touchpoint_nmos_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRolePath.html - web::json::value make_nc_role_path_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Context NMOS resource"), nmos::fields::nc::resource, U("NcTouchpointResourceNmos"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Touchpoint class for NMOS resources"), U("NcTouchpointNmos"), fields, U("NcTouchpoint"), value::null()); + } - return details::make_nc_datatype_typedef(U("Role path"), U("NcRolePath"), true, U("NcString"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmosChannelMapping.html + web::json::value make_nc_touchpoint_nmos_channel_mapping_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTimeInterval.html - web::json::value make_nc_time_interval_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Context Channel Mapping resource"), nmos::fields::nc::resource,U("NcTouchpointResourceNmosChannelMapping"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Touchpoint class for NMOS IS-08 resources"), U("NcTouchpointNmosChannelMapping"), fields, U("NcTouchpoint"), value::null()); + } - return details::make_nc_datatype_typedef(U("Time interval described in nanoseconds"), U("NcTimeInterval"), false, U("NcInt64"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResource.html + web::json::value make_nc_touchpoint_resource_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpoint.html - web::json::value make_nc_touchpoint_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("The type of the resource"), nmos::fields::nc::resource_type, U("NcString"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Touchpoint resource class"), U("NcTouchpointResource"), fields, value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Context namespace"), nmos::fields::nc::context_namespace, U("NcString"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Base touchpoint class"), U("NcTouchpoint"), fields, value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmos.html + web::json::value make_nc_touchpoint_resource_nmos_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmos.html - web::json::value make_nc_touchpoint_nmos_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("NMOS resource UUID"), nmos::fields::nc::id, U("NcUuid"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Touchpoint resource class for NMOS resources"), U("NcTouchpointResourceNmos"), fields, U("NcTouchpointResource"), value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Context NMOS resource"), nmos::fields::nc::resource, U("NcTouchpointResourceNmos"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Touchpoint class for NMOS resources"), U("NcTouchpointNmos"), fields, U("NcTouchpoint"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmosChannelMapping.html + web::json::value make_nc_touchpoint_resource_nmos_channel_mapping_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmosChannelMapping.html - web::json::value make_nc_touchpoint_nmos_channel_mapping_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("IS-08 Audio Channel Mapping input or output id"), nmos::fields::nc::io_id, U("NcString"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Touchpoint resource class for NMOS resources"), U("NcTouchpointResourceNmosChannelMapping"), fields, U("NcTouchpointResourceNmos"), value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Context Channel Mapping resource"), nmos::fields::nc::resource,U("NcTouchpointResourceNmosChannelMapping"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Touchpoint class for NMOS IS-08 resources"), U("NcTouchpointNmosChannelMapping"), fields, U("NcTouchpoint"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUri.html + web::json::value make_nc_uri_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResource.html - web::json::value make_nc_touchpoint_resource_datatype() - { - using web::json::value; + return details::make_nc_datatype_typedef(U("Uniform resource identifier"), U("NcUri"), false, U("NcString"), value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("The type of the resource"), nmos::fields::nc::resource_type, U("NcString"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Touchpoint resource class"), U("NcTouchpointResource"), fields, value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUuid.html + web::json::value make_nc_uuid_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmos.html - web::json::value make_nc_touchpoint_resource_nmos_datatype() - { - using web::json::value; + return details::make_nc_datatype_typedef(U("UUID"), U("NcUuid"), false, U("NcString"), value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("NMOS resource UUID"), nmos::fields::nc::id, U("NcUuid"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Touchpoint resource class for NMOS resources"), U("NcTouchpointResourceNmos"), fields, U("NcTouchpointResource"), value::null()); - } + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcVersionCode.html + web::json::value make_nc_version_code_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmosChannelMapping.html - web::json::value make_nc_touchpoint_resource_nmos_channel_mapping_datatype() - { - using web::json::value; + return details::make_nc_datatype_typedef(U("Version code in semantic versioning format"), U("NcVersionCode"), false, U("NcString"), value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("IS-08 Audio Channel Mapping input or output id"), nmos::fields::nc::io_id, U("NcString"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Touchpoint resource class for NMOS resources"), U("NcTouchpointResourceNmosChannelMapping"), fields, U("NcTouchpointResourceNmos"), value::null()); - } + // Monitoring datatype defintions + // + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncconnectionstatus + web::json::value make_nc_connection_status_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUri.html - web::json::value make_nc_uri_datatype() - { - using web::json::value; + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_connection_status::status::inactive)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and healthy"), U("Healthy"), nc_connection_status::status::healthy)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and partially healthy"), U("PartiallyHealthy"), nc_connection_status::status::partially_healthy)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and unhealthy"), U("Unhealthy"), nc_connection_status::status::unhealthy)); + return details::make_nc_datatype_descriptor_enum(U("Connection status enum data type"), U("NcConnectionStatus"), items, value::null()); + } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nccounter + web::json::value make_nc_counter_datatype() + { + using web::json::value; - return details::make_nc_datatype_typedef(U("Uniform resource identifier"), U("NcUri"), false, U("NcString"), value::null()); - } + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Counter name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Counter value"), nmos::fields::nc::value, U("NcUint64"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Description"), nmos::fields::nc::description, U("NcString"), true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Counter data type"), U("NcCounter"), fields, value::null()); + } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncessencestatus + web::json::value make_nc_essence_status_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUuid.html - web::json::value make_nc_uuid_datatype() - { - using web::json::value; + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_essence_status::status::inactive)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and healthy"), U("Healthy"), nc_essence_status::status::healthy)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and partially healthy"), U("PartiallyHealthy"), nc_essence_status::status::partially_healthy)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and unhealthy"), U("Unhealthy"), nc_essence_status::status::unhealthy)); + return details::make_nc_datatype_descriptor_enum(U("Essence status enum data type"), U("NcEssenceStatus"), items, value::null()); + } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nclinkstatus + web::json::value make_nc_link_status_datatype() + { + using web::json::value; - return details::make_nc_datatype_typedef(U("UUID"), U("NcUuid"), false, U("NcString"), value::null()); - } + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("All the associated network interfaces are up"), U("AllUp"), nc_link_status::status::all_up)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Some of the associated network interfaces are down"), U("SomeDown"), nc_link_status::status::some_down)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("All the associated network interfaces are down"), U("AllDown"), nc_link_status::status::all_down)); + return details::make_nc_datatype_descriptor_enum(U("Link status enum data type"), U("NcLinkStatus"), items, value::null()); + } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncoverallstatus + web::json::value make_nc_overall_status_datatype() + { + using web::json::value; - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcVersionCode.html - web::json::value make_nc_version_code_datatype() - { - using web::json::value; + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_overall_status::status::inactive)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("The overall status is healthy"), U("Healthy"), nc_overall_status::status::healthy)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("The overall status is partially healthy"), U("PartiallyHealthy"), nc_overall_status::status::partially_healthy)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("The overall status is unhealthy"), U("Unhealthy"), nc_overall_status::status::unhealthy)); + return details::make_nc_datatype_descriptor_enum(U("Overall status enum data type"), U("NcOverallStatus"), items, value::null()); + } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsynchronizationstatus + web::json::value make_nc_synchronization_status_datatype() + { + using web::json::value; - return details::make_nc_datatype_typedef(U("Version code in semantic versioning format"), U("NcVersionCode"), false, U("NcString"), value::null()); - } + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Feature not in use"), U("NotUsed"), nc_synchronization_status::status::not_used)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Locked to a synchronization source"), U("Healthy"), nc_synchronization_status::status::healthy)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Partially locked to a synchronization source"), U("PartiallyHealthy"), nc_synchronization_status::status::partially_healthy)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Not locked to a synchronization source"), U("Unhealthy"), nc_synchronization_status::status::unhealthy)); + return details::make_nc_datatype_descriptor_enum(U("Synchronization status enum data type"), U("NcSynchronizationStatus"), items, value::null()); + } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstreamstatus + web::json::value make_nc_stream_status_datatype() + { + using web::json::value; - // Monitoring datatype defintions - // - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncconnectionstatus - web::json::value make_nc_connection_status_datatype() - { - using web::json::value; - - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_connection_status::status::inactive)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and healthy"), U("Healthy"), nc_connection_status::status::healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and partially healthy"), U("PartiallyHealthy"), nc_connection_status::status::partially_healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and unhealthy"), U("Unhealthy"), nc_connection_status::status::unhealthy)); - return details::make_nc_datatype_descriptor_enum(U("Connection status enum data type"), U("NcConnectionStatus"), items, value::null()); - } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nccounter - web::json::value make_nc_counter_datatype() - { - using web::json::value; + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_stream_status::status::inactive)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and healthy"), U("Healthy"), nc_stream_status::status::healthy)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and partially healthy"), U("PartiallyHealthy"), nc_stream_status::status::partially_healthy)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and unhealthy"), U("Unhealthy"), nc_stream_status::status::unhealthy)); + return details::make_nc_datatype_descriptor_enum(U("Stream status enum data type"), U("NcStreamStatus"), items, value::null()); + } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nctransmissionstatus + web::json::value make_nc_transmission_status_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Counter name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Counter value"), nmos::fields::nc::value, U("NcUint64"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Description"), nmos::fields::nc::description, U("NcString"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Counter data type"), U("NcCounter"), fields, value::null()); - } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncessencestatus - web::json::value make_nc_essence_status_datatype() - { - using web::json::value; - - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_essence_status::status::inactive)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and healthy"), U("Healthy"), nc_essence_status::status::healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and partially healthy"), U("PartiallyHealthy"), nc_essence_status::status::partially_healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and unhealthy"), U("Unhealthy"), nc_essence_status::status::unhealthy)); - return details::make_nc_datatype_descriptor_enum(U("Essence status enum data type"), U("NcEssenceStatus"), items, value::null()); - } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nclinkstatus - web::json::value make_nc_link_status_datatype() - { - using web::json::value; + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_transmission_status::status::inactive)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and healthy"), U("Healthy"), nc_transmission_status::status::healthy)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and partially healthy"), U("PartiallyHealthy"), nc_transmission_status::status::partially_healthy)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and unhealthy"), U("Unhealthy"), nc_transmission_status::status::unhealthy)); + return details::make_nc_datatype_descriptor_enum(U("Transmission status enum data type"), U("NcTransmissionStatus"), items, value::null()); + } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncmethodresultcounters + web::json::value make_nc_method_result_counters_datatype() + { + using web::json::value; - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("All the associated network interfaces are up"), U("AllUp"), nc_link_status::status::all_up)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Some of the associated network interfaces are down"), U("SomeDown"), nc_link_status::status::some_down)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("All the associated network interfaces are down"), U("AllDown"), nc_link_status::status::all_down)); - return details::make_nc_datatype_descriptor_enum(U("Link status enum data type"), U("NcLinkStatus"), items, value::null()); - } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncoverallstatus - web::json::value make_nc_overall_status_datatype() - { - using web::json::value; - - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_overall_status::status::inactive)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("The overall status is healthy"), U("Healthy"), nc_overall_status::status::healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("The overall status is partially healthy"), U("PartiallyHealthy"), nc_overall_status::status::partially_healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("The overall status is unhealthy"), U("Unhealthy"), nc_overall_status::status::unhealthy)); - return details::make_nc_datatype_descriptor_enum(U("Overall status enum data type"), U("NcOverallStatus"), items, value::null()); - } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsynchronizationstatus - web::json::value make_nc_synchronization_status_datatype() - { - using web::json::value; - - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Feature not in use"), U("NotUsed"), nc_synchronization_status::status::not_used)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Locked to a synchronization source"), U("Healthy"), nc_synchronization_status::status::healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Partially locked to a synchronization source"), U("PartiallyHealthy"), nc_synchronization_status::status::partially_healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Not locked to a synchronization source"), U("Unhealthy"), nc_synchronization_status::status::unhealthy)); - return details::make_nc_datatype_descriptor_enum(U("Synchronization status enum data type"), U("NcSynchronizationStatus"), items, value::null()); - } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstreamstatus - web::json::value make_nc_stream_status_datatype() - { - using web::json::value; - - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_stream_status::status::inactive)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and healthy"), U("Healthy"), nc_stream_status::status::healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and partially healthy"), U("PartiallyHealthy"), nc_stream_status::status::partially_healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and unhealthy"), U("Unhealthy"), nc_stream_status::status::unhealthy)); - return details::make_nc_datatype_descriptor_enum(U("Stream status enum data type"), U("NcStreamStatus"), items, value::null()); - } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nctransmissionstatus - web::json::value make_nc_transmission_status_datatype() - { - using web::json::value; - - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_transmission_status::status::inactive)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and healthy"), U("Healthy"), nc_transmission_status::status::healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and partially healthy"), U("PartiallyHealthy"), nc_transmission_status::status::partially_healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and unhealthy"), U("Unhealthy"), nc_transmission_status::status::unhealthy)); - return details::make_nc_datatype_descriptor_enum(U("Transmission status enum data type"), U("NcTransmissionStatus"), items, value::null()); - } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncmethodresultcounters - web::json::value make_nc_method_result_counters_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Counters"), nmos::fields::nc::value, U("NcCounter"), false, true, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Counter method result"), U("NcMethodResultCounters"), fields, U("NcMethodResult"), value::null()); + } - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Counters"), nmos::fields::nc::value, U("NcCounter"), false, true, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Counter method result"), U("NcMethodResultCounters"), fields, U("NcMethodResult"), value::null()); - } + // Device Configuration datatypes + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestoremode + web::json::value make_nc_restore_mode_datatype() + { + using web::json::value; - // Device Configuration datatypes - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestoremode - web::json::value make_nc_restore_mode_datatype() - { - using web::json::value; + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore mode is Modify"), U("Modify"), 0)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore mode is Rebuild"), U("Rebuild"), 1)); - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore mode is Modify"), U("Modify"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore mode is Rebuild"), U("Rebuild"), 1)); + return details::make_nc_datatype_descriptor_enum(U("Restore mode enumeration"), U("NcRestoreMode"), items, value::null()); + } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyholder + web::json::value make_nc_property_holder_datatype() + { + using web::json::value; - return details::make_nc_datatype_descriptor_enum(U("Restore mode enumeration"), U("NcRestoreMode"), items, value::null()); - } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyholder - web::json::value make_nc_property_holder_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property descriptor"), nmos::fields::nc::descriptor, U("NcPropertyDescriptor"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property descriptor"), nmos::fields::nc::descriptor, U("NcPropertyDescriptor"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Property holder descriptor"), U("NcPropertyHolder"), fields, value::null()); + } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiesholder + web::json::value make_nc_object_properties_holder_datatype() + { + using web::json::value; - return details::make_nc_datatype_descriptor_struct(U("Property holder descriptor"), U("NcPropertyHolder"), fields, value::null()); - } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiesholder - web::json::value make_nc_object_properties_holder_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Object role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Sequence of role paths which are a dependency for this object"), nmos::fields::nc::dependency_paths, U("NcRolePath"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Sequence of class ids allowed as members of the block"), nmos::fields::nc::allowed_members_classes, U("NcClassId"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Object properties"), nmos::fields::nc::values, U("NcPropertyHolder"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Describes if the object is rebuildable"), nmos::fields::nc::is_rebuildable, U("NcBoolean"), false, false, value::null())); - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Object role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Sequence of role paths which are a dependency for this object"), nmos::fields::nc::dependency_paths, U("NcRolePath"), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Sequence of class ids allowed as members of the block"), nmos::fields::nc::allowed_members_classes, U("NcClassId"), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Object properties"), nmos::fields::nc::values, U("NcPropertyHolder"), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Describes if the object is rebuildable"), nmos::fields::nc::is_rebuildable, U("NcBoolean"), false, false, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Object properties holder descriptor"), U("NcObjectPropertiesHolder"), fields, value::null()); + } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesholder + web::json::value make_nc_bulk_properties_holder_datatype() + { + using web::json::value; - return details::make_nc_datatype_descriptor_struct(U("Object properties holder descriptor"), U("NcObjectPropertiesHolder"), fields, value::null()); - } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesholder - web::json::value make_nc_bulk_properties_holder_datatype() - { - using web::json::value; + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional vendor specific fingerprinting mechanism used for validation purposes"), nmos::fields::nc::validation_fingerprint, U("NcString"), true, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Values by rolePath"), nmos::fields::nc::values, U("NcObjectPropertiesHolder"), false, true, value::null())); - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional vendor specific fingerprinting mechanism used for validation purposes"), nmos::fields::nc::validation_fingerprint, U("NcString"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Values by rolePath"), nmos::fields::nc::values, U("NcObjectPropertiesHolder"), false, true, value::null())); + return details::make_nc_datatype_descriptor_struct(U("Bulk properties holder descriptor"), U("NcBulkPropertiesHolder"), fields, value::null()); + } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestorevalidationstatus + web::json::value make_nc_restore_validation_status_datatype() + { + using web::json::value; - return details::make_nc_datatype_descriptor_struct(U("Bulk properties holder descriptor"), U("NcBulkPropertiesHolder"), fields, value::null()); - } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestorevalidationstatus - web::json::value make_nc_restore_validation_status_datatype() - { - using web::json::value; - - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore was successful"), U("Ok"), 200)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed"), U("Failed"), 400)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed because the role path is not found in the device model or the device cannot create the role path from the data set"), U("NotFound"), 404)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed due to an internal device error preventing the restore from happening"), U("DeviceError"), 500)); - return details::make_nc_datatype_descriptor_enum(U("Restore validation status enumeration"), U("NcRestoreValidationStatus"), items, value::null()); - } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenoticetype - web::json::value make_nc_property_restore_notice_type_datatype() - { - using web::json::value; + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore was successful"), U("Ok"), 200)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed"), U("Failed"), 400)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed because the role path is not found in the device model or the device cannot create the role path from the data set"), U("NotFound"), 404)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed due to an internal device error preventing the restore from happening"), U("DeviceError"), 500)); + return details::make_nc_datatype_descriptor_enum(U("Restore validation status enumeration"), U("NcRestoreValidationStatus"), items, value::null()); + } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenoticetype + web::json::value make_nc_property_restore_notice_type_datatype() + { + using web::json::value; - auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Warning property restore notice"), U("Warning"), nc_property_restore_notice_type::warning)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Error property restore notice"), U("Error"), nc_property_restore_notice_type::error)); - return details::make_nc_datatype_descriptor_enum(U("Property restore notice type enumeration"), U("NcPropertyRestoreNoticeType"), items, value::null()); - } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenotice - web::json::value make_nc_property_restore_notice_datatype() - { - using web::json::value; + auto items = value::array(); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Warning property restore notice"), U("Warning"), nc_property_restore_notice_type::warning)); + web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Error property restore notice"), U("Error"), nc_property_restore_notice_type::error)); + return details::make_nc_datatype_descriptor_enum(U("Property restore notice type enumeration"), U("NcPropertyRestoreNoticeType"), items, value::null()); + } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenotice + web::json::value make_nc_property_restore_notice_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property name"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property restore notice type"), nmos::fields::nc::notice_type, U("NcPropertyRestoreNoticeType"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property restore notice message"), nmos::fields::nc::notice_message, U("NcString"), false, false, value::null())); + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property name"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property restore notice type"), nmos::fields::nc::notice_type, U("NcPropertyRestoreNoticeType"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Property restore notice message"), nmos::fields::nc::notice_message, U("NcString"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Property restore notice descriptor"), U("NcPropertyRestoreNotice"), fields, value::null()); - } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiessetvalidation - web::json::value make_nc_object_properties_set_validation_datatype() - { - using web::json::value; + return details::make_nc_datatype_descriptor_struct(U("Property restore notice descriptor"), U("NcPropertyRestoreNotice"), fields, value::null()); + } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiessetvalidation + web::json::value make_nc_object_properties_set_validation_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Object role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation status"), nmos::fields::nc::status, U("NcRestoreValidationStatus"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation property notices"), nmos::fields::nc::notices, U("NcPropertyRestoreNotice"), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation status message"), nmos::fields::nc::status_message, U("NcString"), true, false, value::null())); + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Object role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation status"), nmos::fields::nc::status, U("NcRestoreValidationStatus"), false, false, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation property notices"), nmos::fields::nc::notices, U("NcPropertyRestoreNotice"), false, true, value::null())); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation status message"), nmos::fields::nc::status_message, U("NcString"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Object properties set validation descriptor"), U("NcObjectPropertiesSetValidation"), fields, value::null()); - } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncmethodresultbulkpropertiesholder - web::json::value make_nc_method_result_bulk_properties_holder_datatype() - { - using web::json::value; + return details::make_nc_datatype_descriptor_struct(U("Object properties set validation descriptor"), U("NcObjectPropertiesSetValidation"), fields, value::null()); + } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncmethodresultbulkpropertiesholder + web::json::value make_nc_method_result_bulk_properties_holder_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Bulk properties holder value"), nmos::fields::nc::value, U("NcBulkPropertiesHolder"), false, false, value::null())); + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Bulk properties holder value"), nmos::fields::nc::value, U("NcBulkPropertiesHolder"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Method result containing bulk properties holder descriptor"), U("NcMethodResultBulkPropertiesHolder"), fields, U("NcMethodResult"), value::null()); - } - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncmethodresultobjectpropertiessetvalidation - web::json::value make_nc_method_result_object_properties_set_validation_datatype() - { - using web::json::value; + return details::make_nc_datatype_descriptor_struct(U("Method result containing bulk properties holder descriptor"), U("NcMethodResultBulkPropertiesHolder"), fields, U("NcMethodResult"), value::null()); + } + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncmethodresultobjectpropertiessetvalidation + web::json::value make_nc_method_result_object_properties_set_validation_datatype() + { + using web::json::value; - auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Object properties set path validation"), nmos::fields::nc::value, U("NcObjectPropertiesSetValidation"), false, true, value::null())); + auto fields = value::array(); + web::json::push_back(fields, details::make_nc_field_descriptor(U("Object properties set path validation"), nmos::fields::nc::value, U("NcObjectPropertiesSetValidation"), false, true, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Method result containing object properties set validation descriptor"), U("NcMethodResultObjectPropertiesSetValidation"), fields, U("NcMethodResult"), value::null()); + return details::make_nc_datatype_descriptor_struct(U("Method result containing object properties set validation descriptor"), U("NcMethodResultObjectPropertiesSetValidation"), fields, U("NcMethodResult"), value::null()); + } } } diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index 12fe818f4..cb096ec45 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -42,464 +42,466 @@ namespace nmos struct control_protocol_state; } - namespace details + namespace nc { - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethodresult - web::json::value make_nc_method_result(const nc_method_result& method_result); - web::json::value make_nc_method_result_error(const nc_method_result& method_result, const utility::string_t& error_message); - web::json::value make_nc_method_result(const nc_method_result& method_result, const web::json::value& value); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncelementid - web::json::value make_nc_element_id(const nc_element_id& element_id); - nc_element_id parse_nc_element_id(const web::json::value& element_id); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventid - web::json::value make_nc_event_id(const nc_event_id& event_id); - nc_event_id parse_nc_event_id(const web::json::value& event_id); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethodid - web::json::value make_nc_method_id(const nc_method_id& method_id); - nc_method_id parse_nc_method_id(const web::json::value& method_id); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyid - web::json::value make_nc_property_id(const nc_property_id& property_id); - nc_property_id parse_nc_property_id(const web::json::value& property_id); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassid - web::json::value make_nc_class_id(const nc_class_id& class_id); - nc_class_id parse_nc_class_id(const web::json::array& class_id); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanufacturer - web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id, const web::uri& website); - web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id); - web::json::value make_nc_manufacturer(const utility::string_t& name); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncproduct - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, - const utility::string_t& brand_name, const nc_uuid& uuid, const utility::string_t& description); - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, - const utility::string_t& brand_name, const nc_uuid& uuid); - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, - const utility::string_t& brand_name); - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdeviceoperationalstate - // device_specific_details can be null - web::json::value make_nc_device_operational_state(nc_device_generic_state::state generic_state, const web::json::value& device_specific_details); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblockmemberdescriptor - web::json::value make_nc_block_member_descriptor(const utility::string_t& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const utility::string_t& user_label, nc_oid owner); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassdescriptor - web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); - web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncenumitemdescriptor - web::json::value make_nc_enum_item_descriptor(const utility::string_t& description, const nc_name& name, uint16_t val); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventdescriptor - web::json::value make_nc_event_descriptor(const utility::string_t& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncfielddescriptor - // constraints can be null - web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); - web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethoddescriptor - // sequence parameters - web::json::value make_nc_method_descriptor(const utility::string_t& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterdescriptor - // constraints can be null - web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints); - web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertydescriptor - // constraints can be null - web::json::value make_nc_property_descriptor(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, - bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorenum - // constraints can be null - // items: sequence - web::json::value make_nc_datatype_descriptor_enum(const utility::string_t& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorprimitive - // constraints can be null - web::json::value make_nc_datatype_descriptor_primitive(const utility::string_t& description, const nc_name& name, const web::json::value& constraints); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorstruct - // constraints can be null - // fields: sequence - web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const utility::string_t& parent_type, const web::json::value& constraints); - web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const web::json::value& constraints); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptortypedef - web::json::value make_nc_datatype_typedef(const utility::string_t& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraints - web::json::value make_nc_property_constraints(const nc_property_id& property_id, const web::json::value& default_value); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsnumber - web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step); - web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t minimum, uint64_t maximum, uint64_t step); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsstring - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern); - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters, const nc_regex& pattern); - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters); - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const nc_regex& pattern); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraints - web::json::value make_nc_parameter_constraints(const web::json::value& default_value); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber - web::json::value make_nc_parameter_constraints_number(uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step); - web::json::value make_nc_parameter_constraints_number(uint64_t minimum, uint64_t maximum, uint64_t step); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring - web::json::value make_nc_parameter_constraints_string(const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern); - web::json::value make_nc_parameter_constraints_string(uint32_t max_characters, const nc_regex& pattern); - web::json::value make_nc_parameter_constraints_string(uint32_t max_characters); - web::json::value make_nc_parameter_constraints_string(const nc_regex& pattern); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpoint - web::json::value make_nc_touchpoint(const utility::string_t& context_namespace); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointnmos - web::json::value make_nc_touchpoint_nmos(const nc_touchpoint_resource_nmos& resource); - - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointnmoschannelmapping - web::json::value make_nc_touchpoint_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource); + namespace details + { + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethodresult + web::json::value make_nc_method_result(const nc_method_result& method_result); + web::json::value make_nc_method_result_error(const nc_method_result& method_result, const utility::string_t& error_message); + web::json::value make_nc_method_result(const nc_method_result& method_result, const web::json::value& value); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncelementid + web::json::value make_nc_element_id(const nc_element_id& element_id); + nc_element_id parse_nc_element_id(const web::json::value& element_id); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventid + web::json::value make_nc_event_id(const nc_event_id& event_id); + nc_event_id parse_nc_event_id(const web::json::value& event_id); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethodid + web::json::value make_nc_method_id(const nc_method_id& method_id); + nc_method_id parse_nc_method_id(const web::json::value& method_id); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyid + web::json::value make_nc_property_id(const nc_property_id& property_id); + nc_property_id parse_nc_property_id(const web::json::value& property_id); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassid + web::json::value make_nc_class_id(const nc_class_id& class_id); + nc_class_id parse_nc_class_id(const web::json::array& class_id); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanufacturer + web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id, const web::uri& website); + web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id); + web::json::value make_nc_manufacturer(const utility::string_t& name); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncproduct + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const utility::string_t& brand_name, const nc_uuid& uuid, const utility::string_t& description); + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const utility::string_t& brand_name, const nc_uuid& uuid); + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const utility::string_t& brand_name); + web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdeviceoperationalstate + // device_specific_details can be null + web::json::value make_nc_device_operational_state(nc_device_generic_state::state generic_state, const web::json::value& device_specific_details); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblockmemberdescriptor + web::json::value make_nc_block_member_descriptor(const utility::string_t& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const utility::string_t& user_label, nc_oid owner); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassdescriptor + web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); + web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncenumitemdescriptor + web::json::value make_nc_enum_item_descriptor(const utility::string_t& description, const nc_name& name, uint16_t val); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventdescriptor + web::json::value make_nc_event_descriptor(const utility::string_t& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncfielddescriptor + // constraints can be null + web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); + web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject - web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethoddescriptor + // sequence parameters + web::json::value make_nc_method_descriptor(const utility::string_t& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock - web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterdescriptor + // constraints can be null + web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints); + web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker - web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertydescriptor + // constraints can be null + web::json::value make_nc_property_descriptor(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, + bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstatusmonitor - web::json::value make_nc_status_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, uint64_t status_reporting_delay); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorenum + // constraints can be null + // items: sequence + web::json::value make_nc_datatype_descriptor_enum(const utility::string_t& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor - web::json::value make_receiver_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, nc_link_status::status link_status, const utility::string_t& link_status_message, nc_connection_status::status connection_status, const utility::string_t& connection_status_message, nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, const web::json::value& synchronization_source_id, nc_stream_status::status stream_status, const utility::string_t& stream_status_message, uint32_t status_reporting_delay, bool auto_reset_monitor); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorprimitive + // constraints can be null + web::json::value make_nc_datatype_descriptor_primitive(const utility::string_t& description, const nc_name& name, const web::json::value& constraints); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorstruct + // constraints can be null + // fields: sequence + web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const utility::string_t& parent_type, const web::json::value& constraints); + web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const web::json::value& constraints); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptortypedef + web::json::value make_nc_datatype_typedef(const utility::string_t& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraints + web::json::value make_nc_property_constraints(const nc_property_id& property_id, const web::json::value& default_value); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsnumber + web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step); + web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t minimum, uint64_t maximum, uint64_t step); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsstring + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern); + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters, const nc_regex& pattern); + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters); + web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const nc_regex& pattern); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraints + web::json::value make_nc_parameter_constraints(const web::json::value& default_value); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber + web::json::value make_nc_parameter_constraints_number(uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step); + web::json::value make_nc_parameter_constraints_number(uint64_t minimum, uint64_t maximum, uint64_t step); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring + web::json::value make_nc_parameter_constraints_string(const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern); + web::json::value make_nc_parameter_constraints_string(uint32_t max_characters, const nc_regex& pattern); + web::json::value make_nc_parameter_constraints_string(uint32_t max_characters); + web::json::value make_nc_parameter_constraints_string(const nc_regex& pattern); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpoint + web::json::value make_nc_touchpoint(const utility::string_t& context_namespace); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointnmos + web::json::value make_nc_touchpoint_nmos(const nc_touchpoint_resource_nmos& resource); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointnmoschannelmapping + web::json::value make_nc_touchpoint_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject + web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock + web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker + web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled); + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstatusmonitor + web::json::value make_nc_status_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, uint64_t status_reporting_delay); + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor + web::json::value make_receiver_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, nc_link_status::status link_status, const utility::string_t& link_status_message, nc_connection_status::status connection_status, const utility::string_t& connection_status_message, nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, const web::json::value& synchronization_source_id, nc_stream_status::status stream_status, const utility::string_t& stream_status_message, uint32_t status_reporting_delay, bool auto_reset_monitor); + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsendermonitor + web::json::value make_sender_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, nc_link_status::status link_status, const utility::string_t& link_status_message, nc_transmission_status::status transmission_status, const utility::string_t& transmission_status_message, nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, const web::json::value& synchronization_source_id, nc_essence_status::status essence_status, const utility::string_t& essence_status_message, uint32_t status_reporting_delay, bool auto_reset_monitor); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager + web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager + web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, + const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, + const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause); + + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager + web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state); + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager + web::json::value make_nc_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesholder + web::json::value make_nc_bulk_properties_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders); + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyholder + web::json::value make_nc_property_holder(const nc_property_id& property_id, const web::json::value& descriptor, const web::json::value& property_value); + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiesholder + web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_holders, const web::json::array& dependency_paths, const web::json::array& allowed_members_classes, bool is_rebuildable); + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenotice + web::json::value make_nc_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message); + + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiessetvalidation + web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const web::json::value& status_message); + } + + // command message response + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#command-response-message-type + web::json::value make_control_protocol_response(int32_t handle, const web::json::value& method_result); + web::json::value make_control_protocol_command_response(const web::json::value& responses); + + // subscription response + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#subscription-response-message-type + web::json::value make_control_protocol_subscription_response(const web::json::value& subscriptions); + + // notification + // See https://specs.amwa.tv/ms-05-01/branches/v1.0.x/docs/Core_Mechanisms.html#notification-messages + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#notification-message-type + web::json::value make_control_protocol_notification(nc_oid oid, const nc_event_id& event_id, const nc_property_changed_event_data& property_changed_event_data); + web::json::value make_control_protocol_notification_message(const web::json::value& notifications); + + // property changed notification event + // See https://specs.amwa.tv/ms-05-01/branches/v1.0.x/docs/Core_Mechanisms.html#the-propertychanged-event + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/NcObject.html#propertychanged-event + web::json::value make_property_changed_event(nc_oid oid, const std::vector& property_changed_event_data_list); + + // error message + // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#error-messages + web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message); + + // Control class models + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/#control-class-models-for-branch-v10-dev + // + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.html + web::json::value make_nc_object_class(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.1.html + web::json::value make_nc_block_class(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.2.html + web::json::value make_nc_worker_class(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.html + web::json::value make_nc_manager_class(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.1.html + web::json::value make_nc_device_manager_class(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.2.html + web::json::value make_nc_class_manager_class(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon + web::json::value make_nc_ident_beacon_class(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor + web::json::value make_nc_receiver_monitor_class(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsendermonitor - web::json::value make_sender_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, nc_link_status::status link_status, const utility::string_t& link_status_message, nc_transmission_status::status transmission_status, const utility::string_t& transmission_status_message, nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, const web::json::value& synchronization_source_id, nc_essence_status::status essence_status, const utility::string_t& essence_status_message, uint32_t status_reporting_delay, bool auto_reset_monitor); + web::json::value make_nc_sender_monitor_class(); + // control classes properties/methods/events + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject + web::json::value make_nc_object_properties(); + web::json::value make_nc_object_methods(); + web::json::value make_nc_object_events(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock + web::json::value make_nc_block_properties(); + web::json::value make_nc_block_methods(); + web::json::value make_nc_block_events(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker + web::json::value make_nc_worker_properties(); + web::json::value make_nc_worker_methods(); + web::json::value make_nc_worker_events(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager - web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); - + web::json::value make_nc_manager_properties(); + web::json::value make_nc_manager_methods(); + web::json::value make_nc_manager_events(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager - web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, - const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, - const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause); - + web::json::value make_nc_device_manager_properties(); + web::json::value make_nc_device_manager_methods(); + web::json::value make_nc_device_manager_events(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state); - - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager - web::json::value make_nc_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); + web::json::value make_nc_class_manager_properties(); + web::json::value make_nc_class_manager_methods(); + web::json::value make_nc_class_manager_events(); + // Monitoring feature set control classes + // https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstatusmonitor + web::json::value make_nc_status_monitor_properties(); + web::json::value make_nc_status_monitor_methods(); + web::json::value make_nc_status_monitor_events(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor + web::json::value make_nc_receiver_monitor_properties(); + web::json::value make_nc_receiver_monitor_methods(); + web::json::value make_nc_receiver_monitor_events(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsendermonitor + web::json::value make_nc_sender_monitor_properties(); + web::json::value make_nc_sender_monitor_methods(); + web::json::value make_nc_sender_monitor_events(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesholder - web::json::value make_nc_bulk_properties_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders); + // Identification feature set control classes + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon + web::json::value make_nc_ident_beacon_properties(); + web::json::value make_nc_ident_beacon_methods(); + web::json::value make_nc_ident_beacon_events(); + // Device configuration classes + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager + web::json::value make_nc_bulk_properties_manager_properties(); + web::json::value make_nc_bulk_properties_manager_methods(); + web::json::value make_nc_bulk_properties_manager_events(); + + // Datatype models + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/#datatype-models-for-branch-v10-dev + // + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_boolean_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_int16_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_int32_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_int64_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_uint16_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_uint32_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_uint64_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_float32_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_float64_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives + web::json::value make_nc_string_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcBlockMemberDescriptor.html + web::json::value make_nc_block_member_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassDescriptor.html + web::json::value make_nc_class_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassId.html + web::json::value make_nc_class_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptor.html + web::json::value make_nc_datatype_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorEnum.html + web::json::value make_nc_datatype_descriptor_enum_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorPrimitive.html + web::json::value make_nc_datatype_descriptor_primitive_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorStruct.html + web::json::value make_nc_datatype_descriptor_struct_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorTypeDef.html + web::json::value make_nc_datatype_descriptor_type_def_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeType.html + web::json::value make_nc_datatype_type_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDescriptor.html + web::json::value make_nc_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceGenericState.html + web::json::value make_nc_device_generic_state_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceOperationalState.html + web::json::value make_nc_device_operational_state_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcElementId.html + web::json::value make_nc_element_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEnumItemDescriptor.html + web::json::value make_nc_enum_item_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventDescriptor.html + web::json::value make_nc_event_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventId.html + web::json::value make_nc_event_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcFieldDescriptor.html + web::json::value make_nc_field_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcId.html + web::json::value make_nc_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcManufacturer.html + web::json::value make_nc_manufacturer_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodDescriptor.html + web::json::value make_nc_method_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodId.html + web::json::value make_nc_method_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResult.html + web::json::value make_nc_method_result_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultBlockMemberDescriptors.html + web::json::value make_nc_method_result_block_member_descriptors_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultClassDescriptor.html + web::json::value make_nc_method_result_class_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultDatatypeDescriptor.html + web::json::value make_nc_method_result_datatype_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultError.html + web::json::value make_nc_method_result_error_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultId.html + web::json::value make_nc_method_result_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultLength.html + web::json::value make_nc_method_result_length_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultPropertyValue.html + web::json::value make_nc_method_result_property_value_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodStatus.html + web::json::value make_nc_method_status_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcName.html + web::json::value make_nc_name_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOid.html + web::json::value make_nc_oid_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOrganizationId.html + web::json::value make_nc_organization_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraints.html + web::json::value make_nc_parameter_constraints_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsNumber.html + web::json::value make_nc_parameter_constraints_number_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsString.html + web::json::value make_nc_parameter_constraints_string_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterDescriptor.html + web::json::value make_nc_parameter_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcProduct.html + web::json::value make_nc_product_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangeType.html + web::json::value make_nc_property_change_type_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangedEventData.html + web::json::value make_nc_property_changed_event_data_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraints.html + web::json::value make_nc_property_contraints_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsNumber.html + web::json::value make_nc_property_constraints_number_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsString.html + web::json::value make_nc_property_constraints_string_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyDescriptor.html + web::json::value make_nc_property_descriptor_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyId.html + web::json::value make_nc_property_id_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRegex.html + web::json::value make_nc_regex_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcResetCause.html + web::json::value make_nc_reset_cause_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRolePath.html + web::json::value make_nc_role_path_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTimeInterval.html + web::json::value make_nc_time_interval_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpoint.html + web::json::value make_nc_touchpoint_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmos.html + web::json::value make_nc_touchpoint_nmos_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmosChannelMapping.html + web::json::value make_nc_touchpoint_nmos_channel_mapping_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResource.html + web::json::value make_nc_touchpoint_resource_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmos.html + web::json::value make_nc_touchpoint_resource_nmos_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmosChannelMapping.html + web::json::value make_nc_touchpoint_resource_nmos_channel_mapping_datatype(); + // See // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUri.html + web::json::value make_nc_uri_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUuid.html + web::json::value make_nc_uuid_datatype(); + // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcVersionCode.html + web::json::value make_nc_version_code_datatype(); + + // Monitoring feature set datatypes + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#datatypes + // + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncconnectionstatus + web::json::value make_nc_connection_status_datatype(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncessencestatus + web::json::value make_nc_essence_status_datatype(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncoverallstatus + web::json::value make_nc_overall_status_datatype(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nclinkstatus + web::json::value make_nc_link_status_datatype(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsynchronizationstatus + web::json::value make_nc_synchronization_status_datatype(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstreamstatus + web::json::value make_nc_stream_status_datatype(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nccounter + web::json::value make_nc_counter_datatype(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nctransmissionstatus + web::json::value make_nc_transmission_status_datatype(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncmethodresultcounters + web::json::value make_nc_method_result_counters_datatype(); + + // Device configuration feature set datatypes + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestoremode + web::json::value make_nc_restore_mode_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyholder - web::json::value make_nc_property_holder(const nc_property_id& property_id, const web::json::value& descriptor, const web::json::value& property_value); - + web::json::value make_nc_property_holder_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiesholder - web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_holders, const web::json::array& dependency_paths, const web::json::array& allowed_members_classes, bool is_rebuildable); - + web::json::value make_nc_object_properties_holder_datatype(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesholder + web::json::value make_nc_bulk_properties_holder_datatype(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestorevalidationstatus + web::json::value make_nc_restore_validation_status_datatype(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenoticetype + web::json::value make_nc_property_restore_notice_type_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenotice - web::json::value make_nc_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message); - + web::json::value make_nc_property_restore_notice_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiessetvalidation - web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const web::json::value& status_message); + web::json::value make_nc_object_properties_set_validation_datatype(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncmethodresultbulkpropertiesholder + web::json::value make_nc_method_result_bulk_properties_holder_datatype(); + // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncmethodresultobjectpropertiessetvalidation + web::json::value make_nc_method_result_object_properties_set_validation_datatype(); } - - // command message response - // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#command-response-message-type - web::json::value make_control_protocol_response(int32_t handle, const web::json::value& method_result); - web::json::value make_control_protocol_command_response(const web::json::value& responses); - - // subscription response - // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#subscription-response-message-type - web::json::value make_control_protocol_subscription_response(const web::json::value& subscriptions); - - // notification - // See https://specs.amwa.tv/ms-05-01/branches/v1.0.x/docs/Core_Mechanisms.html#notification-messages - // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#notification-message-type - web::json::value make_control_protocol_notification(nc_oid oid, const nc_event_id& event_id, const nc_property_changed_event_data& property_changed_event_data); - web::json::value make_control_protocol_notification_message(const web::json::value& notifications); - - // property changed notification event - // See https://specs.amwa.tv/ms-05-01/branches/v1.0.x/docs/Core_Mechanisms.html#the-propertychanged-event - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/NcObject.html#propertychanged-event - web::json::value make_property_changed_event(nc_oid oid, const std::vector& property_changed_event_data_list); - - // error message - // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#error-messages - web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message); - - // Control class models - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/#control-class-models-for-branch-v10-dev - // - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.html - web::json::value make_nc_object_class(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.1.html - web::json::value make_nc_block_class(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.2.html - web::json::value make_nc_worker_class(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.html - web::json::value make_nc_manager_class(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.1.html - web::json::value make_nc_device_manager_class(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.2.html - web::json::value make_nc_class_manager_class(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon - web::json::value make_nc_ident_beacon_class(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor - web::json::value make_nc_receiver_monitor_class(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsendermonitor - web::json::value make_nc_sender_monitor_class(); - - // control classes properties/methods/events - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject - web::json::value make_nc_object_properties(); - web::json::value make_nc_object_methods(); - web::json::value make_nc_object_events(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock - web::json::value make_nc_block_properties(); - web::json::value make_nc_block_methods(); - web::json::value make_nc_block_events(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker - web::json::value make_nc_worker_properties(); - web::json::value make_nc_worker_methods(); - web::json::value make_nc_worker_events(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager - web::json::value make_nc_manager_properties(); - web::json::value make_nc_manager_methods(); - web::json::value make_nc_manager_events(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager - web::json::value make_nc_device_manager_properties(); - web::json::value make_nc_device_manager_methods(); - web::json::value make_nc_device_manager_events(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager_properties(); - web::json::value make_nc_class_manager_methods(); - web::json::value make_nc_class_manager_events(); - // Monitoring feature set control classes - // https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstatusmonitor - web::json::value make_nc_status_monitor_properties(); - web::json::value make_nc_status_monitor_methods(); - web::json::value make_nc_status_monitor_events(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor - web::json::value make_nc_receiver_monitor_properties(); - web::json::value make_nc_receiver_monitor_methods(); - web::json::value make_nc_receiver_monitor_events(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsendermonitor - web::json::value make_nc_sender_monitor_properties(); - web::json::value make_nc_sender_monitor_methods(); - web::json::value make_nc_sender_monitor_events(); - - // Identification feature set control classes - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon - web::json::value make_nc_ident_beacon_properties(); - web::json::value make_nc_ident_beacon_methods(); - web::json::value make_nc_ident_beacon_events(); - - // Device configuration classes - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager - web::json::value make_nc_bulk_properties_manager_properties(); - web::json::value make_nc_bulk_properties_manager_methods(); - web::json::value make_nc_bulk_properties_manager_events(); - - // Datatype models - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/#datatype-models-for-branch-v10-dev - // - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_boolean_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_int16_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_int32_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_int64_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_uint16_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_uint32_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_uint64_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_float32_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_float64_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_string_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcBlockMemberDescriptor.html - web::json::value make_nc_block_member_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassDescriptor.html - web::json::value make_nc_class_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassId.html - web::json::value make_nc_class_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptor.html - web::json::value make_nc_datatype_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorEnum.html - web::json::value make_nc_datatype_descriptor_enum_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorPrimitive.html - web::json::value make_nc_datatype_descriptor_primitive_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorStruct.html - web::json::value make_nc_datatype_descriptor_struct_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorTypeDef.html - web::json::value make_nc_datatype_descriptor_type_def_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeType.html - web::json::value make_nc_datatype_type_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDescriptor.html - web::json::value make_nc_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceGenericState.html - web::json::value make_nc_device_generic_state_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceOperationalState.html - web::json::value make_nc_device_operational_state_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcElementId.html - web::json::value make_nc_element_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEnumItemDescriptor.html - web::json::value make_nc_enum_item_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventDescriptor.html - web::json::value make_nc_event_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventId.html - web::json::value make_nc_event_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcFieldDescriptor.html - web::json::value make_nc_field_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcId.html - web::json::value make_nc_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcManufacturer.html - web::json::value make_nc_manufacturer_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodDescriptor.html - web::json::value make_nc_method_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodId.html - web::json::value make_nc_method_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResult.html - web::json::value make_nc_method_result_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultBlockMemberDescriptors.html - web::json::value make_nc_method_result_block_member_descriptors_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultClassDescriptor.html - web::json::value make_nc_method_result_class_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultDatatypeDescriptor.html - web::json::value make_nc_method_result_datatype_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultError.html - web::json::value make_nc_method_result_error_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultId.html - web::json::value make_nc_method_result_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultLength.html - web::json::value make_nc_method_result_length_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultPropertyValue.html - web::json::value make_nc_method_result_property_value_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodStatus.html - web::json::value make_nc_method_status_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcName.html - web::json::value make_nc_name_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOid.html - web::json::value make_nc_oid_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOrganizationId.html - web::json::value make_nc_organization_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraints.html - web::json::value make_nc_parameter_constraints_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsNumber.html - web::json::value make_nc_parameter_constraints_number_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsString.html - web::json::value make_nc_parameter_constraints_string_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterDescriptor.html - web::json::value make_nc_parameter_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcProduct.html - web::json::value make_nc_product_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangeType.html - web::json::value make_nc_property_change_type_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangedEventData.html - web::json::value make_nc_property_changed_event_data_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraints.html - web::json::value make_nc_property_contraints_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsNumber.html - web::json::value make_nc_property_constraints_number_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsString.html - web::json::value make_nc_property_constraints_string_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyDescriptor.html - web::json::value make_nc_property_descriptor_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyId.html - web::json::value make_nc_property_id_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRegex.html - web::json::value make_nc_regex_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcResetCause.html - web::json::value make_nc_reset_cause_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRolePath.html - web::json::value make_nc_role_path_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTimeInterval.html - web::json::value make_nc_time_interval_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpoint.html - web::json::value make_nc_touchpoint_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmos.html - web::json::value make_nc_touchpoint_nmos_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmosChannelMapping.html - web::json::value make_nc_touchpoint_nmos_channel_mapping_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResource.html - web::json::value make_nc_touchpoint_resource_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmos.html - web::json::value make_nc_touchpoint_resource_nmos_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmosChannelMapping.html - web::json::value make_nc_touchpoint_resource_nmos_channel_mapping_datatype(); - // See // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUri.html - web::json::value make_nc_uri_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUuid.html - web::json::value make_nc_uuid_datatype(); - // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcVersionCode.html - web::json::value make_nc_version_code_datatype(); - - // Monitoring feature set datatypes - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#datatypes - // - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncconnectionstatus - web::json::value make_nc_connection_status_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncessencestatus - web::json::value make_nc_essence_status_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncoverallstatus - web::json::value make_nc_overall_status_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nclinkstatus - web::json::value make_nc_link_status_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsynchronizationstatus - web::json::value make_nc_synchronization_status_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstreamstatus - web::json::value make_nc_stream_status_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nccounter - web::json::value make_nc_counter_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nctransmissionstatus - web::json::value make_nc_transmission_status_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncmethodresultcounters - web::json::value make_nc_method_result_counters_datatype(); - - // Device configuration feature set datatypes - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestoremode - web::json::value make_nc_restore_mode_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyholder - web::json::value make_nc_property_holder_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiesholder - web::json::value make_nc_object_properties_holder_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesholder - web::json::value make_nc_bulk_properties_holder_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestorevalidationstatus - web::json::value make_nc_restore_validation_status_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenoticetype - web::json::value make_nc_property_restore_notice_type_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenotice - web::json::value make_nc_property_restore_notice_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiessetvalidation - web::json::value make_nc_object_properties_set_validation_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncmethodresultbulkpropertiesholder - web::json::value make_nc_method_result_bulk_properties_holder_datatype(); - // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncmethodresultobjectpropertiessetvalidation - web::json::value make_nc_method_result_object_properties_set_validation_datatype(); } - #endif diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index 8cdcf58e4..5d143ea05 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -13,7 +13,7 @@ namespace nmos { using web::json::value; - auto data = details::make_nc_block(nc_block_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true, members); + auto data = nc::details::make_nc_block(nc_block_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true, members); return{ is12_versions::v1_0, types::nc_block, std::move(data), true }; } @@ -44,7 +44,7 @@ namespace nmos for(const auto& class_id: allowed_member_classes) { - web::json::push_back(allowed_member_classes_array, nmos::details::make_nc_class_id(class_id)); + web::json::push_back(allowed_member_classes_array, nc::details::make_nc_class_id(class_id)); } control_protocol_resource.data[nmos::fields::nc::allowed_members_classes] = allowed_member_classes_array; @@ -83,14 +83,14 @@ namespace nmos { using web::json::value; - const auto& manufacturer = details::make_nc_manufacturer(nmos::experimental::fields::manufacturer_name(settings)); - const auto& product = details::make_nc_product(nmos::experimental::fields::product_name(settings), nmos::experimental::fields::product_key(settings), nmos::experimental::fields::product_key(settings)); + const auto& manufacturer = nc::details::make_nc_manufacturer(nmos::experimental::fields::manufacturer_name(settings)); + const auto& product = nc::details::make_nc_product(nmos::experimental::fields::product_name(settings), nmos::experimental::fields::product_key(settings), nmos::experimental::fields::product_key(settings)); const auto& serial_number = nmos::experimental::fields::serial_number(settings); const auto device_name = value::null(); const auto device_role = value::null(); - const auto& operational_state = details::make_nc_device_operational_state(nc_device_generic_state::normal_operation, value::null()); + const auto& operational_state = nc::details::make_nc_device_operational_state(nc_device_generic_state::normal_operation, value::null()); - auto data = details::make_nc_device_manager(oid, root_block_oid, value::string(U("Device manager")), U("The device manager offers information about the product this device is representing"), value::null(), value::null(), + auto data = nc::details::make_nc_device_manager(oid, root_block_oid, value::string(U("Device manager")), U("The device manager offers information about the product this device is representing"), value::null(), value::null(), manufacturer, product, serial_number, value::null(), device_name, device_role, operational_state, nc_reset_cause::unknown); return{ is12_versions::v1_0, types::nc_device_manager, std::move(data), true }; @@ -101,7 +101,7 @@ namespace nmos { using web::json::value; - auto data = details::make_nc_class_manager(oid, root_block_oid, value::string(U("Class manager")), U("The class manager offers access to control class and data type descriptors"), value::null(), value::null(), control_protocol_state); + auto data = nc::details::make_nc_class_manager(oid, root_block_oid, value::string(U("Class manager")), U("The class manager offers access to control class and data type descriptors"), value::null(), value::null(), control_protocol_state); return{ is12_versions::v1_0, types::nc_class_manager, std::move(data), true }; } @@ -112,7 +112,7 @@ namespace nmos control_protocol_resource make_receiver_monitor(nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, nc_link_status::status link_status, const utility::string_t& link_status_message, nc_connection_status::status connection_status, const utility::string_t& connection_status_message, nc_synchronization_status::status synchronization_status, const utility::string_t& synchronization_status_message, const web::json::value& synchronization_source_id, nc_stream_status::status stream_status, const utility::string_t& stream_status_message, uint32_t status_reporting_delay, bool auto_reset_monitor) { - auto data = details::make_receiver_monitor(nc_receiver_monitor_class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints, enabled, overall_status, overall_status_message, link_status, link_status_message, connection_status, connection_status_message, synchronization_status, synchronization_status_message, synchronization_source_id, stream_status, stream_status_message, status_reporting_delay, auto_reset_monitor); + auto data = nc::details::make_receiver_monitor(nc_receiver_monitor_class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints, enabled, overall_status, overall_status_message, link_status, link_status_message, connection_status, connection_status_message, synchronization_status, synchronization_status_message, synchronization_source_id, stream_status, stream_status_message, status_reporting_delay, auto_reset_monitor); return{ is12_versions::v1_0, types::nc_status_monitor, std::move(data), true }; } @@ -121,7 +121,7 @@ namespace nmos control_protocol_resource make_sender_monitor(nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, nc_link_status::status link_status, const utility::string_t& link_status_message, nc_transmission_status::status transmission_status, const utility::string_t& transmission_status_message, nc_synchronization_status::status synchronization_status, const utility::string_t& synchronization_status_message, const web::json::value& synchronization_source_id, nc_essence_status::status essence_status, const utility::string_t& essence_status_message, uint32_t status_reporting_delay, bool auto_reset_counters) { - auto data = details::make_sender_monitor(nc_sender_monitor_class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints, enabled, overall_status, overall_status_message, link_status, link_status_message, transmission_status, transmission_status_message, synchronization_status, synchronization_status_message, synchronization_source_id, essence_status, essence_status_message, status_reporting_delay, auto_reset_counters); + auto data = nc::details::make_sender_monitor(nc_sender_monitor_class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints, enabled, overall_status, overall_status_message, link_status, link_status_message, transmission_status, transmission_status_message, synchronization_status, synchronization_status_message, synchronization_source_id, essence_status, essence_status_message, status_reporting_delay, auto_reset_counters); return{ is12_versions::v1_0, types::nc_status_monitor, std::move(data), true }; } @@ -134,7 +134,7 @@ namespace nmos { using web::json::value; - auto data = nmos::details::make_nc_worker(nc_ident_beacon_class_id, oid, constant_oid, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, enabled); + auto data = nc::details::make_nc_worker(nc_ident_beacon_class_id, oid, constant_oid, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, enabled); data[nmos::fields::nc::active] = value::boolean(active); return{ is12_versions::v1_0, types::nc_ident_beacon, std::move(data), true }; @@ -147,7 +147,7 @@ namespace nmos { using web::json::value; - auto data = details::make_nc_bulk_properties_manager(oid, root_block_oid, value::string(U("Bulk properties manager")), U("The bulk properties manager offers a central model for getting and setting properties of multiple role paths"), value::null(), value::null()); + auto data = nc::details::make_nc_bulk_properties_manager(oid, root_block_oid, value::string(U("Bulk properties manager")), U("The bulk properties manager offers a central model for getting and setting properties of multiple role paths"), value::null(), value::null()); return{ is12_versions::v1_0, types::nc_bulk_properties_manager, std::move(data), true }; } diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 836d855d6..93ca5c20e 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -54,13 +54,13 @@ namespace nmos // create control class property descriptor web::json::value make_control_class_property_descriptor(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) { - return nmos::details::make_nc_property_descriptor(description, id, name, type_name, is_read_only, is_nullable, is_sequence, is_deprecated, constraints); + return nc::details::make_nc_property_descriptor(description, id, name, type_name, is_read_only, is_nullable, is_sequence, is_deprecated, constraints); } // create control class method parameter descriptor web::json::value make_control_class_method_parameter_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { - return nmos::details::make_nc_parameter_descriptor(description, name, type_name, is_nullable, is_sequence, constraints); + return nc::details::make_nc_parameter_descriptor(description, name, type_name, is_nullable, is_sequence, constraints); } namespace details @@ -72,7 +72,7 @@ namespace nmos value parameters = value::array(); for (const auto& parameter : parameters_) { web::json::push_back(parameters, parameter); } - return nmos::details::make_nc_method_descriptor(description, id, name, result_datatype, parameters, is_deprecated); + return nc::details::make_nc_method_descriptor(description, id, name, result_datatype, parameters, is_deprecated); } } // create control class method descriptor @@ -84,7 +84,7 @@ namespace nmos // create control class event descriptor web::json::value make_control_class_event_descriptor(const utility::string_t& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated) { - return nmos::details::make_nc_event_descriptor(description, id, name, event_datatype, is_deprecated); + return nc::details::make_nc_event_descriptor(description, id, name, event_datatype, is_deprecated); } namespace details @@ -200,10 +200,10 @@ namespace nmos if (data_set.is_null()) { - return nmos::details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("Null dataSet parameter")); + return nc::details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("Null dataSet parameter")); } - auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); + auto result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); if (get_read_only_modification_allow_list && remove_device_model_object && create_device_model_object) { result = validate_set_properties_by_path(resources, resource, data_set, recurse, static_cast(restore_mode), get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); @@ -211,7 +211,7 @@ namespace nmos const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) { - return nmos::details::make_nc_method_result({ nmos::nc_method_status::method_deprecated }, nmos::fields::nc::value(result)); + return nc::details::make_nc_method_result({ nmos::nc_method_status::method_deprecated }, nmos::fields::nc::value(result)); } } return result; @@ -227,10 +227,10 @@ namespace nmos if (data_set.is_null()) { - return nmos::details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("Null dataSet parameter")); + return nc::details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("Null dataSet parameter")); } - auto result = nmos::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("callbacks not implemented")); + auto result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("callbacks not implemented")); if (get_read_only_modification_allow_list && remove_device_model_object && create_device_model_object) { result = set_properties_by_path(resources, resource, data_set, recurse, static_cast(restore_mode), get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); @@ -238,7 +238,7 @@ namespace nmos const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) { - return nmos::details::make_nc_method_result({ nmos::nc_method_status::method_deprecated }, nmos::fields::nc::value(result)); + return nc::details::make_nc_method_result({ nmos::nc_method_status::method_deprecated }, nmos::fields::nc::value(result)); } } return result; @@ -290,7 +290,7 @@ namespace nmos { for (const auto& nc_method_descriptor : nc_method_descriptors.as_array()) { - methods.push_back(make_control_class_method(nc_method_descriptor, method_handlers.at(nmos::details::parse_nc_method_id(nmos::fields::nc::id(nc_method_descriptor))))); + methods.push_back(make_control_class_method(nc_method_descriptor, method_handlers.at(nc::details::parse_nc_method_id(nmos::fields::nc::id(nc_method_descriptor))))); } } return methods; @@ -308,9 +308,9 @@ namespace nmos // NcObject { nc_object_class_id, make_control_class_descriptor(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), // NcObject properties - to_vector(make_nc_object_properties()), + to_vector(nc::make_nc_object_properties()), // NcObject methods - to_methods_vector(make_nc_object_methods(), + to_methods_vector(nc::make_nc_object_methods(), { // link NcObject method_ids with method functions { nc_object_get_method_id, details::make_nc_get_handler(get_control_protocol_class_descriptor) }, @@ -322,13 +322,13 @@ namespace nmos { nc_object_get_sequence_length_method_id, details::make_nc_get_sequence_length_handler(get_control_protocol_class_descriptor) } }), // NcObject events - to_vector(make_nc_object_events())) }, + to_vector(nc::make_nc_object_events())) }, // NcBlock { nc_block_class_id, make_control_class_descriptor(U("NcBlock class descriptor"), nc_block_class_id, U("NcBlock"), // NcBlock properties - to_vector(make_nc_block_properties()), + to_vector(nc::make_nc_block_properties()), // NcBlock methods - to_methods_vector(make_nc_block_methods(), + to_methods_vector(nc::make_nc_block_methods(), { // link NcBlock method_ids with method functions { nc_block_get_member_descriptors_method_id, details::make_nc_get_member_descriptors_handler() }, @@ -337,70 +337,70 @@ namespace nmos { nc_block_find_members_by_class_id_method_id, details::make_nc_find_members_by_class_id_handler() } }), // NcBlock events - to_vector(make_nc_block_events())) }, + to_vector(nc::make_nc_block_events())) }, // NcWorker { nc_worker_class_id, make_control_class_descriptor(U("NcWorker class descriptor"), nc_worker_class_id, U("NcWorker"), // NcWorker properties - to_vector(make_nc_worker_properties()), + to_vector(nc::make_nc_worker_properties()), // NcWorker methods - to_methods_vector(make_nc_worker_methods(), {}), + to_methods_vector(nc::make_nc_worker_methods(), {}), // NcWorker events - to_vector(make_nc_worker_events())) }, + to_vector(nc::make_nc_worker_events())) }, // NcManager { nc_manager_class_id, make_control_class_descriptor(U("NcManager class descriptor"), nc_manager_class_id, U("NcManager"), // NcManager properties - to_vector(make_nc_manager_properties()), + to_vector(nc::make_nc_manager_properties()), // NcManager methods - to_methods_vector(make_nc_manager_methods(), {}), + to_methods_vector(nc::make_nc_manager_methods(), {}), // NcManager events - to_vector(make_nc_manager_events())) }, + to_vector(nc::make_nc_manager_events())) }, // NcDeviceManager { nc_device_manager_class_id, make_control_class_descriptor(U("NcDeviceManager class descriptor"), nc_device_manager_class_id, U("NcDeviceManager"), U("DeviceManager"), // NcDeviceManager properties - to_vector(make_nc_device_manager_properties()), + to_vector(nc::make_nc_device_manager_properties()), // NcDeviceManager methods - to_methods_vector(make_nc_device_manager_methods(), {}), + to_methods_vector(nc::make_nc_device_manager_methods(), {}), // NcDeviceManager events - to_vector(make_nc_device_manager_events())) }, + to_vector(nc::make_nc_device_manager_events())) }, // NcClassManager { nc_class_manager_class_id, make_control_class_descriptor(U("NcClassManager class descriptor"), nc_class_manager_class_id, U("NcClassManager"), U("ClassManager"), // NcClassManager properties - to_vector(make_nc_class_manager_properties()), + to_vector(nc::make_nc_class_manager_properties()), // NcClassManager methods - to_methods_vector(make_nc_class_manager_methods(), + to_methods_vector(nc::make_nc_class_manager_methods(), { // link NcClassManager method_ids with method functions { nc_class_manager_get_control_class_method_id, details::make_nc_get_control_class_handler(get_control_protocol_class_descriptor) }, { nc_class_manager_get_datatype_method_id, details::make_nc_get_datatype_handler(get_control_protocol_datatype_descriptor) } }), // NcClassManager events - to_vector(make_nc_class_manager_events())) }, + to_vector(nc::make_nc_class_manager_events())) }, // Identification feature set // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#control-classes // NcIdentBeacon { nc_ident_beacon_class_id, make_control_class_descriptor(U("NcIdentBeacon class descriptor"), nc_ident_beacon_class_id, U("NcIdentBeacon"), // NcIdentBeacon properties - to_vector(make_nc_ident_beacon_properties()), + to_vector(nc::make_nc_ident_beacon_properties()), // NcIdentBeacon methods - to_methods_vector(make_nc_ident_beacon_methods(), {}), + to_methods_vector(nc::make_nc_ident_beacon_methods(), {}), // NcIdentBeacon events - to_vector(make_nc_ident_beacon_events())) }, + to_vector(nc::make_nc_ident_beacon_events())) }, // Monitoring feature set // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#control-classes // NcStatusMonitor { nc_status_monitor_class_id, make_control_class_descriptor(U("NcStatusMonitor class descriptor"), nc_status_monitor_class_id, U("NcStatusMonitor"), // NcReceiverMonitor properties - to_vector(make_nc_status_monitor_properties()), + to_vector(nc::make_nc_status_monitor_properties()), // NcReceiverMonitor methods - to_methods_vector(make_nc_status_monitor_methods(), {}), + to_methods_vector(nc::make_nc_status_monitor_methods(), {}), // NcReceiverMonitor events - to_vector(make_nc_status_monitor_events())) }, + to_vector(nc::make_nc_status_monitor_events())) }, // NcReceiverMonitor { nc_receiver_monitor_class_id, make_control_class_descriptor(U("NcReceiverMonitor class descriptor"), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), // NcReceiverMonitor properties - to_vector(make_nc_receiver_monitor_properties()), + to_vector(nc::make_nc_receiver_monitor_properties()), // NcReceiverMonitor methods - to_methods_vector(make_nc_receiver_monitor_methods(), + to_methods_vector(nc::make_nc_receiver_monitor_methods(), { // link NcReceiverMonitor method_ids with method functions { nc_receiver_monitor_get_lost_packet_counters_method_id, details::make_nc_get_lost_packet_counters_handler(get_lost_packet_counters)}, @@ -408,13 +408,13 @@ namespace nmos { nc_receiver_monitor_reset_monitor_method_id, details::make_nc_reset_monitor_handler(get_control_protocol_class_descriptor, property_changed, reset_monitor)} }), // NcReceiverMonitor events - to_vector(make_nc_receiver_monitor_events())) }, + to_vector(nc::make_nc_receiver_monitor_events())) }, // NcSenderMonitor { nc_sender_monitor_class_id, make_control_class_descriptor(U("NcSenderMonitor class descriptor"), nc_sender_monitor_class_id, U("NcSenderMonitor"), // NcSenderMonitor properties - to_vector(make_nc_sender_monitor_properties()), + to_vector(nc::make_nc_sender_monitor_properties()), // NcSenderMonitor methods - to_methods_vector(make_nc_sender_monitor_methods(), + to_methods_vector(nc::make_nc_sender_monitor_methods(), { // link NcSenderMonitor method_ids with method functions // TODO: implement actual GetTransmissionError and ResetCountersAndMessages function @@ -422,17 +422,17 @@ namespace nmos { nc_sender_monitor_reset_monitor_method_id, details::make_nc_reset_monitor_handler(get_control_protocol_class_descriptor, property_changed, reset_monitor)} }), // NcSenderMonitor events - to_vector(make_nc_sender_monitor_events())) }, + to_vector(nc::make_nc_sender_monitor_events())) }, // NcBulkPropertiesManager { nc_bulk_properties_manager_class_id, make_control_class_descriptor(U("NcBulkPropertiesManager class descriptor"), nc_bulk_properties_manager_class_id, U("NcBulkPropertiesManager"), U("BulkPropertiesManager"), - to_vector(make_nc_bulk_properties_manager_properties()), - to_methods_vector(make_nc_bulk_properties_manager_methods(), + to_vector(nc::make_nc_bulk_properties_manager_properties()), + to_methods_vector(nc::make_nc_bulk_properties_manager_methods(), { { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), create_validation_fingerprint)}, { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object) }, { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object) } }), - to_vector(make_nc_bulk_properties_manager_events())) } + to_vector(nc::make_nc_bulk_properties_manager_events())) } }; // setup the standard datatypes @@ -440,97 +440,97 @@ namespace nmos { // Datatype models // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/ - { U("NcBoolean"), {make_nc_boolean_datatype()} }, - { U("NcInt16"), {make_nc_int16_datatype()} }, - { U("NcInt32"), {make_nc_int32_datatype()} }, - { U("NcInt64"), {make_nc_int64_datatype()} }, - { U("NcUint16"), {make_nc_uint16_datatype()} }, - { U("NcUint32"), {make_nc_uint32_datatype()} }, - { U("NcUint64"), {make_nc_uint64_datatype()} }, - { U("NcFloat32"), {make_nc_float32_datatype()} }, - { U("NcFloat64"), {make_nc_float64_datatype()} }, - { U("NcString"), {make_nc_string_datatype()} }, - { U("NcClassId"), {make_nc_class_id_datatype()} }, - { U("NcOid"), {make_nc_oid_datatype()} }, - { U("NcTouchpoint"), {make_nc_touchpoint_datatype()} }, - { U("NcElementId"), {make_nc_element_id_datatype()} }, - { U("NcPropertyId"), {make_nc_property_id_datatype()} }, - { U("NcPropertyConstraints"), {make_nc_property_contraints_datatype()} }, - { U("NcMethodResultPropertyValue"), {make_nc_method_result_property_value_datatype()} }, - { U("NcMethodStatus"), {make_nc_method_status_datatype()} }, - { U("NcMethodResult"), {make_nc_method_result_datatype()} }, - { U("NcId"), {make_nc_id_datatype()} }, - { U("NcMethodResultId"), {make_nc_method_result_id_datatype()} }, - { U("NcMethodResultLength"), {make_nc_method_result_length_datatype()} }, - { U("NcPropertyChangeType"), {make_nc_property_change_type_datatype()} }, - { U("NcPropertyChangedEventData"), {make_nc_property_changed_event_data_datatype()} }, - { U("NcDescriptor"), {make_nc_descriptor_datatype()} }, - { U("NcBlockMemberDescriptor"), {make_nc_block_member_descriptor_datatype()} }, - { U("NcMethodResultBlockMemberDescriptors"), {make_nc_method_result_block_member_descriptors_datatype()} }, - { U("NcVersionCode"), {make_nc_version_code_datatype()} }, - { U("NcOrganizationId"), {make_nc_organization_id_datatype()} }, - { U("NcUri"), {make_nc_uri_datatype()} }, - { U("NcManufacturer"), {make_nc_manufacturer_datatype()} }, - { U("NcUuid"), {make_nc_uuid_datatype()} }, - { U("NcProduct"), {make_nc_product_datatype()} }, - { U("NcDeviceGenericState"), {make_nc_device_generic_state_datatype()} }, - { U("NcDeviceOperationalState"), {make_nc_device_operational_state_datatype()} }, - { U("NcResetCause"), {make_nc_reset_cause_datatype()} }, - { U("NcName"), {make_nc_name_datatype()} }, - { U("NcPropertyDescriptor"), {make_nc_property_descriptor_datatype()} }, - { U("NcParameterDescriptor"), {make_nc_parameter_descriptor_datatype()} }, - { U("NcMethodId"), {make_nc_method_id_datatype()} }, - { U("NcMethodDescriptor"), {make_nc_method_descriptor_datatype()} }, - { U("NcEventId"), {make_nc_event_id_datatype()} }, - { U("NcEventDescriptor"), {make_nc_event_descriptor_datatype()} }, - { U("NcClassDescriptor"), {make_nc_class_descriptor_datatype()} }, - { U("NcParameterConstraints"), {make_nc_parameter_constraints_datatype()} }, - { U("NcDatatypeType"), {make_nc_datatype_type_datatype()} }, - { U("NcDatatypeDescriptor"), {make_nc_datatype_descriptor_datatype()} }, - { U("NcMethodResultClassDescriptor"), {make_nc_method_result_class_descriptor_datatype()} }, - { U("NcMethodResultDatatypeDescriptor"), {make_nc_method_result_datatype_descriptor_datatype()} }, - { U("NcMethodResultError"), {make_nc_method_result_error_datatype()} }, - { U("NcDatatypeDescriptorEnum"), {make_nc_datatype_descriptor_enum_datatype()} }, - { U("NcDatatypeDescriptorPrimitive"), {make_nc_datatype_descriptor_primitive_datatype()} }, - { U("NcDatatypeDescriptorStruct"), {make_nc_datatype_descriptor_struct_datatype()} }, - { U("NcDatatypeDescriptorTypeDef"), {make_nc_datatype_descriptor_type_def_datatype()} }, - { U("NcEnumItemDescriptor"), {make_nc_enum_item_descriptor_datatype()} }, - { U("NcFieldDescriptor"), {make_nc_field_descriptor_datatype()} }, - { U("NcPropertyConstraintsNumber"), {make_nc_property_constraints_number_datatype()} }, - { U("NcPropertyConstraintsString"), {make_nc_property_constraints_string_datatype()} }, - { U("NcRegex"), {make_nc_regex_datatype()} }, - { U("NcRolePath"), {make_nc_role_path_datatype()} }, - { U("NcParameterConstraintsNumber"), {make_nc_parameter_constraints_number_datatype()} }, - { U("NcParameterConstraintsString"), {make_nc_parameter_constraints_string_datatype()} }, - { U("NcTimeInterval"), {make_nc_time_interval_datatype()} }, - { U("NcTouchpointNmos"), {make_nc_touchpoint_nmos_datatype()} }, - { U("NcTouchpointNmosChannelMapping"), {make_nc_touchpoint_nmos_channel_mapping_datatype()} }, - { U("NcTouchpointResource"), {make_nc_touchpoint_resource_datatype()} }, - { U("NcTouchpointResourceNmos"), {make_nc_touchpoint_resource_nmos_datatype()} }, - { U("NcTouchpointResourceNmosChannelMapping"), {make_nc_touchpoint_resource_nmos_channel_mapping_datatype()} }, + { U("NcBoolean"), {nc::make_nc_boolean_datatype()} }, + { U("NcInt16"), {nc::make_nc_int16_datatype()} }, + { U("NcInt32"), {nc::make_nc_int32_datatype()} }, + { U("NcInt64"), {nc::make_nc_int64_datatype()} }, + { U("NcUint16"), {nc::make_nc_uint16_datatype()} }, + { U("NcUint32"), {nc::make_nc_uint32_datatype()} }, + { U("NcUint64"), {nc::make_nc_uint64_datatype()} }, + { U("NcFloat32"), {nc::make_nc_float32_datatype()} }, + { U("NcFloat64"), {nc::make_nc_float64_datatype()} }, + { U("NcString"), {nc::make_nc_string_datatype()} }, + { U("NcClassId"), {nc::make_nc_class_id_datatype()} }, + { U("NcOid"), {nc::make_nc_oid_datatype()} }, + { U("NcTouchpoint"), {nc::make_nc_touchpoint_datatype()} }, + { U("NcElementId"), {nc::make_nc_element_id_datatype()} }, + { U("NcPropertyId"), {nc::make_nc_property_id_datatype()} }, + { U("NcPropertyConstraints"), {nc::make_nc_property_contraints_datatype()} }, + { U("NcMethodResultPropertyValue"), {nc::make_nc_method_result_property_value_datatype()} }, + { U("NcMethodStatus"), {nc::make_nc_method_status_datatype()} }, + { U("NcMethodResult"), {nc::make_nc_method_result_datatype()} }, + { U("NcId"), {nc::make_nc_id_datatype()} }, + { U("NcMethodResultId"), {nc::make_nc_method_result_id_datatype()} }, + { U("NcMethodResultLength"), {nc::make_nc_method_result_length_datatype()} }, + { U("NcPropertyChangeType"), {nc::make_nc_property_change_type_datatype()} }, + { U("NcPropertyChangedEventData"), {nc::make_nc_property_changed_event_data_datatype()} }, + { U("NcDescriptor"), {nc::make_nc_descriptor_datatype()} }, + { U("NcBlockMemberDescriptor"), {nc::make_nc_block_member_descriptor_datatype()} }, + { U("NcMethodResultBlockMemberDescriptors"), {nc::make_nc_method_result_block_member_descriptors_datatype()} }, + { U("NcVersionCode"), {nc::make_nc_version_code_datatype()} }, + { U("NcOrganizationId"), {nc::make_nc_organization_id_datatype()} }, + { U("NcUri"), {nc::make_nc_uri_datatype()} }, + { U("NcManufacturer"), {nc::make_nc_manufacturer_datatype()} }, + { U("NcUuid"), {nc::make_nc_uuid_datatype()} }, + { U("NcProduct"), {nc::make_nc_product_datatype()} }, + { U("NcDeviceGenericState"), {nc::make_nc_device_generic_state_datatype()} }, + { U("NcDeviceOperationalState"), {nc::make_nc_device_operational_state_datatype()} }, + { U("NcResetCause"), {nc::make_nc_reset_cause_datatype()} }, + { U("NcName"), {nc::make_nc_name_datatype()} }, + { U("NcPropertyDescriptor"), {nc::make_nc_property_descriptor_datatype()} }, + { U("NcParameterDescriptor"), {nc::make_nc_parameter_descriptor_datatype()} }, + { U("NcMethodId"), {nc::make_nc_method_id_datatype()} }, + { U("NcMethodDescriptor"), {nc::make_nc_method_descriptor_datatype()} }, + { U("NcEventId"), {nc::make_nc_event_id_datatype()} }, + { U("NcEventDescriptor"), {nc::make_nc_event_descriptor_datatype()} }, + { U("NcClassDescriptor"), {nc::make_nc_class_descriptor_datatype()} }, + { U("NcParameterConstraints"), {nc::make_nc_parameter_constraints_datatype()} }, + { U("NcDatatypeType"), {nc::make_nc_datatype_type_datatype()} }, + { U("NcDatatypeDescriptor"), {nc::make_nc_datatype_descriptor_datatype()} }, + { U("NcMethodResultClassDescriptor"), {nc::make_nc_method_result_class_descriptor_datatype()} }, + { U("NcMethodResultDatatypeDescriptor"), {nc::make_nc_method_result_datatype_descriptor_datatype()} }, + { U("NcMethodResultError"), {nc::make_nc_method_result_error_datatype()} }, + { U("NcDatatypeDescriptorEnum"), {nc::make_nc_datatype_descriptor_enum_datatype()} }, + { U("NcDatatypeDescriptorPrimitive"), {nc::make_nc_datatype_descriptor_primitive_datatype()} }, + { U("NcDatatypeDescriptorStruct"), {nc::make_nc_datatype_descriptor_struct_datatype()} }, + { U("NcDatatypeDescriptorTypeDef"), {nc::make_nc_datatype_descriptor_type_def_datatype()} }, + { U("NcEnumItemDescriptor"), {nc::make_nc_enum_item_descriptor_datatype()} }, + { U("NcFieldDescriptor"), {nc::make_nc_field_descriptor_datatype()} }, + { U("NcPropertyConstraintsNumber"), {nc::make_nc_property_constraints_number_datatype()} }, + { U("NcPropertyConstraintsString"), {nc::make_nc_property_constraints_string_datatype()} }, + { U("NcRegex"), {nc::make_nc_regex_datatype()} }, + { U("NcRolePath"), {nc::make_nc_role_path_datatype()} }, + { U("NcParameterConstraintsNumber"), {nc::make_nc_parameter_constraints_number_datatype()} }, + { U("NcParameterConstraintsString"), {nc::make_nc_parameter_constraints_string_datatype()} }, + { U("NcTimeInterval"), {nc::make_nc_time_interval_datatype()} }, + { U("NcTouchpointNmos"), {nc::make_nc_touchpoint_nmos_datatype()} }, + { U("NcTouchpointNmosChannelMapping"), {nc::make_nc_touchpoint_nmos_channel_mapping_datatype()} }, + { U("NcTouchpointResource"), {nc::make_nc_touchpoint_resource_datatype()} }, + { U("NcTouchpointResourceNmos"), {nc::make_nc_touchpoint_resource_nmos_datatype()} }, + { U("NcTouchpointResourceNmosChannelMapping"), {nc::make_nc_touchpoint_resource_nmos_channel_mapping_datatype()} }, // Monitoring feature set // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#datatypes - { U("NcConnectionStatus"), {make_nc_connection_status_datatype()} }, - { U("NcCounter"), {make_nc_counter_datatype()} }, - { U("NcEssenceStatus"), {make_nc_essence_status_datatype()} }, - { U("NcLinkStatus"), {make_nc_link_status_datatype()} }, - { U("NcMethodResultCounters"), {make_nc_method_result_counters_datatype()} }, - { U("NcOverallStatus"), {make_nc_overall_status_datatype()} }, - { U("NcSynchronizationStatus"), {make_nc_synchronization_status_datatype()} }, - { U("NcStreamStatus"), {make_nc_stream_status_datatype()} }, - { U("NcTransmissionStatus"), {make_nc_transmission_status_datatype()} }, + { U("NcConnectionStatus"), {nc::make_nc_connection_status_datatype()} }, + { U("NcCounter"), {nc::make_nc_counter_datatype()} }, + { U("NcEssenceStatus"), {nc::make_nc_essence_status_datatype()} }, + { U("NcLinkStatus"), {nc::make_nc_link_status_datatype()} }, + { U("NcMethodResultCounters"), {nc::make_nc_method_result_counters_datatype()} }, + { U("NcOverallStatus"), {nc::make_nc_overall_status_datatype()} }, + { U("NcSynchronizationStatus"), {nc::make_nc_synchronization_status_datatype()} }, + { U("NcStreamStatus"), {nc::make_nc_stream_status_datatype()} }, + { U("NcTransmissionStatus"), {nc::make_nc_transmission_status_datatype()} }, // Device configuration feature set // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#datatypes - { U("NcRestoreMode"), {make_nc_restore_mode_datatype()} }, - { U("NcPropertyHolder"), {make_nc_property_holder_datatype()} }, - { U("NcObjectPropertiesHolder"), {make_nc_object_properties_holder_datatype()} }, - { U("NcBulkPropertiesHolder"), {make_nc_bulk_properties_holder_datatype()} }, - { U("NcRestoreValidationStatus"), {make_nc_restore_validation_status_datatype()} }, - { U("NcPropertyRestoreNoticeType"), {make_nc_property_restore_notice_type_datatype()} }, - { U("NcPropertyRestoreNotice"), {make_nc_property_restore_notice_datatype()} }, - { U("NcObjectPropertiesSetValidation"), {make_nc_object_properties_set_validation_datatype()} }, - { U("NcMethodResultBulkPropertiesHolder"), {make_nc_method_result_bulk_properties_holder_datatype()} }, - { U("NcMethodResultObjectPropertiesSetValidation"), {make_nc_method_result_object_properties_set_validation_datatype()} } + { U("NcRestoreMode"), {nc::make_nc_restore_mode_datatype()} }, + { U("NcPropertyHolder"), {nc::make_nc_property_holder_datatype()} }, + { U("NcObjectPropertiesHolder"), {nc::make_nc_object_properties_holder_datatype()} }, + { U("NcBulkPropertiesHolder"), {nc::make_nc_bulk_properties_holder_datatype()} }, + { U("NcRestoreValidationStatus"), {nc::make_nc_restore_validation_status_datatype()} }, + { U("NcPropertyRestoreNoticeType"), {nc::make_nc_property_restore_notice_type_datatype()} }, + { U("NcPropertyRestoreNotice"), {nc::make_nc_property_restore_notice_datatype()} }, + { U("NcObjectPropertiesSetValidation"), {nc::make_nc_object_properties_set_validation_datatype()} }, + { U("NcMethodResultBulkPropertiesHolder"), {nc::make_nc_method_result_bulk_properties_holder_datatype()} }, + { U("NcMethodResultObjectPropertiesSetValidation"), {nc::make_nc_method_result_object_properties_set_validation_datatype()} } }; } diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index aa4348f23..0d4de4529 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -40,7 +40,7 @@ namespace nmos auto& runtime_prop_constraints = runtime_property_constraints.as_array(); auto found_constraints = std::find_if(runtime_prop_constraints.begin(), runtime_prop_constraints.end(), [&property_id](const web::json::value& constraints) { - return property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::property_id(constraints)); + return property_id == parse_nc_property_id(nmos::fields::nc::property_id(constraints)); }); if (runtime_prop_constraints.end() != found_constraints) @@ -362,7 +362,7 @@ namespace nmos } // get the role_path_segement member resource - if (is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(*member_found)))) + if (is_block(parse_nc_class_id(nmos::fields::nc::class_id(*member_found)))) { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(*member_found); @@ -414,7 +414,7 @@ namespace nmos const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != found) { - if (nmos::nc::is_sender_monitor(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) + if (nmos::nc::is_sender_monitor(parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) { return details::update_sender_monitor_overall_status(resources, oid, get_control_protocol_class_descriptor, gate); } @@ -690,7 +690,7 @@ namespace nmos const auto& property_descriptors = control_class.property_descriptors.as_array(); auto found = std::find_if(property_descriptors.begin(), property_descriptors.end(), [&property_id](const web::json::value& property_descriptor) { - return (property_id == nmos::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor))); + return (property_id == nc::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor))); }); if (property_descriptors.end() != found) { return *found; } @@ -717,7 +717,7 @@ namespace nmos // get members on all NcBlock(s) for (const auto& member : members) { - if (is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + if (is_block(nc::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(member); @@ -771,7 +771,7 @@ namespace nmos // do role match on all NcBlock(s) for (const auto& member : members) { - if (is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + if (is_block(nc::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(member); @@ -795,7 +795,7 @@ namespace nmos auto match = [&](const web::json::value& descriptor) { - const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); + const auto& class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); if (include_derived) { return !boost::find_first(class_id, class_id_).empty(); } else { return class_id == class_id_; } @@ -819,7 +819,7 @@ namespace nmos // do class_id match on all NcBlock(s) for (const auto& member : members) { - if (is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + if (is_block(nc::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(member); @@ -842,10 +842,10 @@ namespace nmos auto& parent = nc_block_resource.data; const auto& child = resource.data; - if (!is_block(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(parent)))) throw std::logic_error("non-NcBlock cannot be nested"); + if (!is_block(details::parse_nc_class_id(nmos::fields::nc::class_id(parent)))) throw std::logic_error("non-NcBlock cannot be nested"); web::json::push_back(parent[nmos::fields::nc::members], - nmos::details::make_nc_block_member_descriptor(nmos::fields::description(child), nmos::fields::nc::role(child), nmos::fields::nc::oid(child), nmos::fields::nc::constant_oid(child), nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(child)), nmos::fields::nc::user_label(child), nmos::fields::nc::oid(parent))); + details::make_nc_block_member_descriptor(nmos::fields::description(child), nmos::fields::nc::role(child), nmos::fields::nc::oid(child), nmos::fields::nc::constant_oid(child), nc::details::parse_nc_class_id(nmos::fields::nc::class_id(child)), nmos::fields::nc::user_label(child), nmos::fields::nc::oid(parent))); nc_block_resource.resources.push_back(resource); } @@ -1095,7 +1095,7 @@ namespace nmos if (resources.end() != found) { // find the relevant nc_property_descriptor - const auto& property = nc::find_property_descriptor(property_id, nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(property_id, nc::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)), get_control_protocol_class_descriptor); if (!property.is_null() && found->has_data() && found->data.has_field(nmos::fields::nc::name(property))) { return found->data.at(nmos::fields::nc::name(property)); @@ -1111,7 +1111,7 @@ namespace nmos const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != found) { - const auto& property = nc::find_property_descriptor(property_id, nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(property_id, nc::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)), get_control_protocol_class_descriptor); if (!property.is_null()) { try @@ -1302,9 +1302,9 @@ namespace nmos // Furthermore, after activation, as long as the monitor isn’t being deactivated, it MUST delay the reporting // of non Healthy states for the duration specified by statusReportingDelay, and then transition to any other appropriate state. const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found && nc::is_status_monitor(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) + if (resources.end() != found && nc::is_status_monitor(nc::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) { - const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)); + const auto& class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)); auto activation_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); auto succeed = set_property(resources, oid, nmos::fields::nc::monitor_activation_time, activation_time, gate); @@ -1361,9 +1361,9 @@ namespace nmos bool deactivate_monitor(resources& resources, nc_oid oid, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found && nc::is_status_monitor(nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) + if (resources.end() != found && nc::is_status_monitor(nc::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) { - const auto& class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)); + const auto& class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)); auto succeed = set_property(resources, oid, nmos::fields::nc::monitor_activation_time, web::json::value::number(0), gate); diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index bb748317b..f8d39a6ec 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -246,7 +246,7 @@ namespace nmos const auto oid = nmos::fields::nc::oid(cmd); // get methodId - const auto& method_id = nmos::details::parse_nc_method_id(nmos::fields::nc::method_id(cmd)); + const auto& method_id = nc::details::parse_nc_method_id(nmos::fields::nc::method_id(cmd)); // get arguments const auto& arguments = nmos::fields::nc::arguments(cmd); @@ -256,7 +256,7 @@ namespace nmos auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != resource) { - const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); + const auto class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); // find the relevant method handler to execute // method tuple definition described in control_protocol_handlers.h @@ -280,7 +280,7 @@ namespace nmos utility::ostringstream_t ss; ss << "invalid argument: " << arguments.serialize() << " error: " << e.what(); slog::log(gate, SLOG_FLF) << ss.str(); - nc_method_result = details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + nc_method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); } } else @@ -290,7 +290,7 @@ namespace nmos ss << U("unsupported method_id: ") << nmos::fields::nc::method_id(cmd).serialize() << U(" for control class class_id: ") << resource->data.at(nmos::fields::nc::class_id).serialize(); slog::log(gate, SLOG_FLF) << ss.str(); - nc_method_result = details::make_nc_method_result_error({ nc_method_status::method_not_implemented }, ss.str()); + nc_method_result = nc::details::make_nc_method_result_error({ nc_method_status::method_not_implemented }, ss.str()); } } else @@ -299,10 +299,10 @@ namespace nmos utility::ostringstream_t ss; ss << U("unknown oid: ") << oid; slog::log(gate, SLOG_FLF) << ss.str(); - nc_method_result = details::make_nc_method_result_error({ nc_method_status::bad_oid }, ss.str()); + nc_method_result = nc::details::make_nc_method_result_error({ nc_method_status::bad_oid }, ss.str()); } // accumulating up response - auto response = make_control_protocol_response(handle, nc_method_result); + auto response = nc::make_control_protocol_response(handle, nc_method_result); web::json::push_back(responses, response); } @@ -310,7 +310,7 @@ namespace nmos // add command_response to the grain ready to transfer to the client in nmos::send_control_protocol_ws_messages_thread resources.modify(grain, [&](nmos::resource& grain) { - web::json::push_back(nmos::fields::message_grain_data(grain.data), make_control_protocol_command_response(responses)); + web::json::push_back(nmos::fields::message_grain_data(grain.data), nc::make_control_protocol_command_response(responses)); grain.updated = strictly_increasing_update(resources); }); @@ -347,7 +347,7 @@ namespace nmos // add subscription_response to the grain ready to transfer to the client in nmos::send_control_protocol_ws_messages_thread resources.modify(grain, [&](nmos::resource& grain) { - web::json::push_back(nmos::fields::message_grain_data(grain.data), make_control_protocol_subscription_response(valid_subscriptions)); + web::json::push_back(nmos::fields::message_grain_data(grain.data), nc::make_control_protocol_subscription_response(valid_subscriptions)); grain.updated = strictly_increasing_update(resources); }); @@ -369,7 +369,7 @@ namespace nmos resources.modify(grain, [&](nmos::resource& grain) { web::json::push_back(nmos::fields::message_grain_data(grain.data), - make_control_protocol_error_message({ nc_method_status::bad_command_format }, utility::s2us(e.what()))); + nc::make_control_protocol_error_message({ nc_method_status::bad_command_format }, utility::s2us(e.what()))); grain.updated = strictly_increasing_update(resources); }); @@ -381,7 +381,7 @@ namespace nmos resources.modify(grain, [&](nmos::resource& grain) { web::json::push_back(nmos::fields::message_grain_data(grain.data), - make_control_protocol_error_message({ nc_method_status::bad_command_format }, utility::s2us(std::string("Unexpected exception while handing control protocol command : ") + e.what()))); + nc::make_control_protocol_error_message({ nc_method_status::bad_command_format }, utility::s2us(std::string("Unexpected exception while handing control protocol command : ") + e.what()))); grain.updated = strictly_increasing_update(resources); }); @@ -393,7 +393,7 @@ namespace nmos resources.modify(grain, [&](nmos::resource& grain) { web::json::push_back(nmos::fields::message_grain_data(grain.data), - make_control_protocol_error_message({ nc_method_status::bad_command_format }, U("Unexpected unknown exception while handing control protocol command"))); + nc::make_control_protocol_error_message({ nc_method_status::bad_command_format }, U("Unexpected unknown exception while handing control protocol command"))); grain.updated = strictly_increasing_update(resources); }); diff --git a/Development/nmos/test/configuration_methods_test.cpp b/Development/nmos/test/configuration_methods_test.cpp index fbbf2cf8c..4f954521f 100644 --- a/Development/nmos/test/configuration_methods_test.cpp +++ b/Development/nmos/test/configuration_methods_test.cpp @@ -42,13 +42,13 @@ BST_TEST_CASE(testGetPropertiesByPath) auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); nmos::make_rebuildable(receivers); // root, receivers, mon1 - auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({{nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({{nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); // make monitor1 rebuildable nmos::make_rebuildable(monitor1); - auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + auto monitor_class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 - auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); nmos::nc::push_back(receivers, monitor1); // add example-control to root-block nmos::nc::push_back(receivers, monitor2); diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index c0ece72e8..0ced2c49a 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -60,10 +60,10 @@ BST_TEST_CASE(testIsBlockModified) auto receivers = nmos::make_block(++oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); auto receiver_block_oid = oid; // root, receivers, mon1 - auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); auto monitor_1_oid = oid; // root, receivers, mon2 - auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); auto monitor_2_oid = oid; nmos::nc::push_back(receivers, monitor1); // add example-control to root-block @@ -76,25 +76,25 @@ BST_TEST_CASE(testIsBlockModified) push_back(role_path, U("root")); push_back(role_path, U("receivers")); - const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); // Members unchanged { auto property_holders = value::array(); auto members = value::array(); - const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + const auto class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); { - const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); push_back(members, block_member_descriptor); } { - const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); + const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(!nmos::is_block_modified(receivers, object_properties_holder)); } @@ -104,12 +104,12 @@ BST_TEST_CASE(testIsBlockModified) auto property_holders = value::array(); auto members = value::array(); - const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); - const auto block_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, class_id, U("label"), receiver_block_oid); + const auto class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + const auto block_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, class_id, U("label"), receiver_block_oid); push_back(members, block_descriptor); - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); + const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -119,18 +119,18 @@ BST_TEST_CASE(testIsBlockModified) auto property_holders = value::array(); auto members = value::array(); - const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + const auto class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); { - const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), 10, true, class_id, U("monitor 1"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), 10, true, class_id, U("monitor 1"), receiver_block_oid); push_back(members, block_member_descriptor); } { - const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), 20, true, class_id, U("monitor 2"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), 20, true, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); + const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -140,18 +140,18 @@ BST_TEST_CASE(testIsBlockModified) auto property_holders = value::array(); auto members = value::array(); - const auto class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + const auto class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); { - const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon3"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon3"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); push_back(members, block_member_descriptor); } { - const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon4"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon4"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); + const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -163,16 +163,16 @@ BST_TEST_CASE(testIsBlockModified) auto members = value::array(); const auto class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 1 }); { - const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); push_back(members, block_member_descriptor); } { - const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); + const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -184,16 +184,16 @@ BST_TEST_CASE(testIsBlockModified) auto members = value::array(); const auto class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 1 }); { - const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), 20); + const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), 20); push_back(members, block_member_descriptor); } { - const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), 20); + const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), 20); push_back(members, block_member_descriptor); } - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); + const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -205,16 +205,16 @@ BST_TEST_CASE(testIsBlockModified) auto members = value::array(); const auto class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 1 }); { - const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, false, class_id, U("monitor 1"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, false, class_id, U("monitor 1"), receiver_block_oid); push_back(members, block_member_descriptor); } { - const auto block_member_descriptor = nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, false, class_id, U("monitor 2"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, false, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); + const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -226,7 +226,7 @@ BST_TEST_CASE(testGetObjectPropertiesHolder) using web::json::value_of; using web::json::value; - const auto enabled_property_descriptor = nmos::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, web::json::value::null()); + const auto enabled_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, web::json::value::null()); // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -234,25 +234,25 @@ BST_TEST_CASE(testGetObjectPropertiesHolder) { const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); + const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); } { const auto role_path = value_of({ U("root"), U("receivers"), U("mon2") }); auto property_holders = value::array(); - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); + const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); } { const auto role_path = value_of({ U("root"), U("senders"), U("mon1") }); auto property_holders = value::array(); - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); + const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); } @@ -300,10 +300,10 @@ BST_TEST_CASE(testGetRolePath) auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); nmos::make_rebuildable(receivers); // root, receivers, mon1 - auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); - nmos::nc_class_id monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + nmos::nc_class_id monitor_class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 - auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); nmos::nc::push_back(receivers, monitor1); // add example-control to root-block nmos::nc::push_back(receivers, monitor2); @@ -352,14 +352,14 @@ BST_TEST_CASE(testApplyBackupDataSet) auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); nmos::make_rebuildable(receivers); // root, receivers, mon1 - auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({{nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({{nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); // make monitor1 rebuildable nmos::make_rebuildable(monitor1); auto monitor_1_oid = oid; - auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + auto monitor_class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 - auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); auto monitor_2_oid = oid; nmos::nc::push_back(receivers, monitor1); // add example-control to root-block @@ -396,13 +396,13 @@ BST_TEST_CASE(testApplyBackupDataSet) create_device_model_object_called = true; - auto data = nmos::details::make_nc_object(nmos::nc_receiver_monitor_class_id, oid, true, owner, role, web::json::value::string(user_label), U(""), web::json::value::null(), web::json::value::null()); + auto data = nmos::nc::details::make_nc_object(nmos::nc_receiver_monitor_class_id, oid, true, owner, role, web::json::value::string(user_label), U(""), web::json::value::null(), web::json::value::null()); return nmos::control_protocol_resource({ nmos::is12_versions::v1_0, nmos::types::nc_block, std::move(data), true }); }; - const auto enabled_property_descriptor = nmos::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, web::json::value::null()); - const auto class_id_property_descriptor = nmos::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, web::json::value::null()); + const auto enabled_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, web::json::value::null()); + const auto class_id_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, web::json::value::null()); { // Check the successful modification of the "enabled" flag of mon1's worker base class in Modify mode // @@ -410,9 +410,9 @@ BST_TEST_CASE(testApplyBackupDataSet) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); + const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); const auto target_role_path = value_of({ U("root"), U("receivers")}); bool recurse = true; @@ -435,7 +435,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(!remove_device_model_object_called); BST_CHECK(!create_device_model_object_called); } - const auto connection_status_property_descriptor = nmos::details::make_nc_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::fields::nc::connection_status_message, U("NcString"), true, false, false, false, web::json::value::null()); + const auto connection_status_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::fields::nc::connection_status_message, U("NcString"), true, false, false, false, web::json::value::null()); { // Check get_read_only_modification_allow_list_handler is called when changing a read only property of rebuildable object in Rebuild mode // @@ -448,9 +448,9 @@ BST_TEST_CASE(testApplyBackupDataSet) const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); // This is a read only property - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value(U("change this value"))); + const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value(U("change this value"))); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -487,9 +487,9 @@ BST_TEST_CASE(testApplyBackupDataSet) const auto role_path = value_of({ U("root"), U("receivers"), U("mon2") }); auto property_holders = value::array(); // This is a read only property - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value(U("change this value"))); + const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value(U("change this value"))); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -513,7 +513,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_REQUIRE_EQUAL(1, property_restore_notices.size()); const auto& notice = *property_restore_notices.begin(); - BST_CHECK_EQUAL(nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); + BST_CHECK_EQUAL(nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::nc::details::parse_nc_property_id(nmos::fields::nc::id(notice))); BST_CHECK_EQUAL(nmos::fields::nc::connection_status_message.key, nmos::fields::nc::name(notice)); BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); @@ -537,10 +537,10 @@ BST_TEST_CASE(testApplyBackupDataSet) const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); // This is a read only property - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value(U("change this value")))); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value(U("change this value")))); // This is a writable property - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false))); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false))); + const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -565,7 +565,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_REQUIRE_EQUAL(1, property_restore_notices.size()); const auto& notice = *property_restore_notices.begin(); - BST_CHECK_EQUAL(nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::details::parse_nc_property_id(nmos::fields::nc::id(notice))); + BST_CHECK_EQUAL(nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::nc::details::parse_nc_property_id(nmos::fields::nc::id(notice))); BST_CHECK_EQUAL(nmos::fields::nc::connection_status_message.key, nmos::fields::nc::name(notice)); BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); @@ -574,7 +574,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(!remove_device_model_object_called); BST_CHECK(!create_device_model_object_called); } - const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); { // Check remove_device_model_object_called is called when trying to modify a rebuildable block // @@ -588,9 +588,9 @@ BST_TEST_CASE(testApplyBackupDataSet) auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; @@ -611,7 +611,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(remove_device_model_object_called); BST_CHECK(!create_device_model_object_called); } - const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); { // Check create_device_model_object_called is called when trying to modify a rebuildable block // @@ -626,25 +626,25 @@ BST_TEST_CASE(testApplyBackupDataSet) { auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -697,9 +697,9 @@ BST_TEST_CASE(testApplyBackupDataSet) auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; @@ -739,12 +739,12 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); nmos::make_rebuildable(receivers); // root, receivers, mon1 - auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); nmos::make_rebuildable(monitor1); auto monitor_1_oid = oid; - const auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + const auto monitor_class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 - auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); nmos::nc::push_back(receivers, monitor1); // add example-control to root-block nmos::nc::push_back(receivers, monitor2); @@ -761,7 +761,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) nmos::remove_device_model_object_handler remove_device_model_object; nmos::create_device_model_object_handler create_device_model_object; - const auto enabled_property_descriptor = nmos::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, web::json::value::null()); + const auto enabled_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, web::json::value::null()); { // Check that Modify mode is unaffected by undefined Rebuild mode callbacks // @@ -769,9 +769,9 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); + const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -796,9 +796,9 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); + const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -824,10 +824,10 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); // This is a read only property - const auto property_descriptor = nmos::details::make_nc_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::fields::nc::connection_status_message, U("NcString"), true, false, false, false, web::json::value::null()); - const auto property_holder = nmos::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, property_descriptor, value(U("change this value"))); + const auto property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::fields::nc::connection_status_message, U("NcString"), true, false, false, false, web::json::value::null()); + const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, property_descriptor, value(U("change this value"))); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -847,8 +847,8 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); } { - const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); - const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); // Check undefined create_device_model_object and remove_device_model_object causes an unsupported error when attempting to modify a rebuildable block // // Create Object Properties Holder @@ -857,14 +857,14 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -915,14 +915,14 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); nmos::make_rebuildable(receivers); // root, receivers, mon1 - auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({{nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({{nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); // make monitor1 rebuildable nmos::make_rebuildable(monitor1); auto monitor_1_oid = oid; - auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + auto monitor_class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 - auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); auto monitor_2_oid = oid; nmos::nc::push_back(receivers, monitor1); // add example-control to root-block @@ -959,14 +959,14 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) create_device_model_object_called = true; - auto data = nmos::details::make_nc_object(nmos::nc_receiver_monitor_class_id, oid, true, owner, role, web::json::value::string(user_label), U(""), web::json::value::null(), web::json::value::null()); + auto data = nmos::nc::details::make_nc_object(nmos::nc_receiver_monitor_class_id, oid, true, owner, role, web::json::value::string(user_label), U(""), web::json::value::null(), web::json::value::null()); return nmos::control_protocol_resource({ nmos::is12_versions::v1_0, nmos::types::nc_block, std::move(data), true }); }; - const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); - const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); - const auto class_id_property_descriptor = nmos::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); + const auto class_id_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, web::json::value::null()); { // Handle constant oid clash // @@ -980,25 +980,25 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) { auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -1049,25 +1049,25 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) { auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, false, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, false, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -1142,14 +1142,14 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); nmos::make_rebuildable(receivers); // root, receivers, mon1 - auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); // make monitor1 rebuildable nmos::make_rebuildable(monitor1); auto monitor_1_oid = oid; - auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + auto monitor_class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 - auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); auto monitor_2_oid = oid; nmos::nc::push_back(receivers, monitor1); // add example-control to root-block @@ -1187,8 +1187,8 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) // Simulate error on adding object to device model return nmos::control_protocol_resource(); }; - const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); - const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); { // Check remove_device_model_object_called error is handled when attempting to modify a rebuildable block // @@ -1202,9 +1202,9 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; @@ -1243,14 +1243,14 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -1285,7 +1285,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); } } - const auto class_id_property_descriptor = nmos::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, U("classId"), U("NcClassId"), true, false, false, false, web::json::value::null()); + const auto class_id_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, U("classId"), U("NcClassId"), true, false, false, false, web::json::value::null()); { // Check create_device_model_object_called error is handled when trying to modify a rebuildable block // @@ -1300,31 +1300,31 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) { auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -1386,18 +1386,18 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) auto property_holders2 = value::array(); auto members2 = value::array(); - push_back(members1, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders1, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members1)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders1.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members1, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders1, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members1)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders1.as_array(), value::array().as_array(), value::array().as_array(), false)); // duplicate - push_back(members2, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders2, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members2)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(role_path.as_array(), property_holders2.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members2, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders2, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members2)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders2.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -1451,14 +1451,14 @@ BST_TEST_CASE(testModifyRebuildableBlock) nmos::make_rebuildable(receivers); nmos::set_block_allowed_member_classes(receivers, {nmos::nc_receiver_monitor_class_id}); // root, receivers, mon1 - auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); // make monitor1 rebuildable nmos::make_rebuildable(monitor1); auto monitor_1_oid = oid; - auto monitor_class_id = nmos::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + auto monitor_class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 - auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); auto monitor_2_oid = oid; nmos::nc::push_back(receivers, monitor1); // add example-control to root-block @@ -1495,13 +1495,13 @@ BST_TEST_CASE(testModifyRebuildableBlock) create_device_model_object_called = true; - auto data = nmos::details::make_nc_object(nmos::nc_receiver_monitor_class_id, oid, true, owner, role, web::json::value::string(user_label), U(""), web::json::value::null(), web::json::value::null()); + auto data = nmos::nc::details::make_nc_object(nmos::nc_receiver_monitor_class_id, oid, true, owner, role, web::json::value::string(user_label), U(""), web::json::value::null(), web::json::value::null()); return nmos::control_protocol_resource({ nmos::is12_versions::v1_0, nmos::types::nc_block, std::move(data), true }); }; - const auto block_members_property_descriptor = nmos::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); - const auto oid_property_descriptor = nmos::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); - const auto class_id_property_descriptor = nmos::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); + const auto class_id_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, web::json::value::null()); // No class id specified in the objet properties holder for new monitor causes an error { @@ -1512,26 +1512,26 @@ BST_TEST_CASE(testModifyRebuildableBlock) const auto role_path = value_of({ U("root"), U("receivers") }); { auto members = value::array(); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(block_property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(block_property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); } - const auto block_object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto block_object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, block_object_properties_holder); const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor auto monitor3_property_holders = value::array(); const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { // No property holders, including no class id - push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(monitor3_property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool validate = true; @@ -1571,27 +1571,27 @@ BST_TEST_CASE(testModifyRebuildableBlock) const auto role_path = value_of({ U("root"), U("receivers") }); { auto members = value::array(); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(block_property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(block_property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); } - const auto block_object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto block_object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, block_object_properties_holder); // Create Object Properties Holder for new monitor const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } auto monitor3_property_holders = value::array(); const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { //auto property_holders = value::array(); - push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); - push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::details::make_nc_class_id(nmos::nc_block_class_id))); // disallowed class id - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(monitor3_property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(monitor3_property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_nc_class_id(nmos::nc_block_class_id))); // disallowed class id + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool validate = true; @@ -1630,27 +1630,27 @@ BST_TEST_CASE(testModifyRebuildableBlock) const auto role_path = value_of({ U("root"), U("receivers") }); { auto members = value::array(); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(block_property_holders, nmos::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(block_property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); } - const auto block_object_properties_holder = nmos::details::make_nc_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto block_object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, block_object_properties_holder); // Create Object Properties Holder for new monitor const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } auto monitor3_property_holders = value::array(); const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { //auto property_holders = value::array(); - push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); - push_back(monitor3_property_holders, nmos::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); - push_back(object_properties_holders, nmos::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(monitor3_property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(monitor3_property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); + push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool validate = true; diff --git a/Development/nmos/test/control_protocol_methods_test.cpp b/Development/nmos/test/control_protocol_methods_test.cpp index c235f0496..0fbde5358 100644 --- a/Development/nmos/test/control_protocol_methods_test.cpp +++ b/Development/nmos/test/control_protocol_methods_test.cpp @@ -53,7 +53,7 @@ BST_TEST_CASE(testRemoveSequenceItem) // helper function to create writable_sequence object auto make_writable_sequence = [&writable_value, &writable_sequence_class_id](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description) { - auto data = nmos::details::make_nc_worker(writable_sequence_class_id, oid, true, owner, role, value::string(user_label), description, web::json::value::null(), web::json::value::null(), true); + auto data = nmos::nc::details::make_nc_worker(writable_sequence_class_id, oid, true, owner, role, value::string(user_label), description, web::json::value::null(), web::json::value::null(), true); auto values = value::array(); web::json::push_back(values, value::number(10)); web::json::push_back(values, value::number(9)); diff --git a/Development/nmos/test/control_protocol_test.cpp b/Development/nmos/test/control_protocol_test.cpp index 87ea2d89f..b53d4b436 100644 --- a/Development/nmos/test/control_protocol_test.cpp +++ b/Development/nmos/test/control_protocol_test.cpp @@ -29,7 +29,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false }, { U("constraints"), value::null() } }); - const auto property_class_id_ = nmos::details::make_nc_property_descriptor(U("Static value. All instances of the same class will have the same identity value"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, value::null()); + const auto property_class_id_ = nmos::nc::details::make_nc_property_descriptor(U("Static value. All instances of the same class will have the same identity value"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, value::null()); BST_REQUIRE_EQUAL(property_class_id, property_class_id_); const auto property_oid = value_of({ @@ -46,7 +46,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false }, { U("constraints"), value::null() } }); - const auto property_oid_ = nmos::details::make_nc_property_descriptor(U("Object identifier"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, value::null()); + const auto property_oid_ = nmos::nc::details::make_nc_property_descriptor(U("Object identifier"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, value::null()); BST_REQUIRE_EQUAL(property_oid, property_oid_); const auto property_constant_oid = value_of({ @@ -63,7 +63,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false }, { U("constraints"), value::null() } }); - const auto property_constant_oid_ = nmos::details::make_nc_property_descriptor(U("TRUE iff OID is hardwired into device"), nmos::nc_object_constant_oid_property_id, nmos::fields::nc::constant_oid, U("NcBoolean"), true, false, false, false, value::null()); + const auto property_constant_oid_ = nmos::nc::details::make_nc_property_descriptor(U("TRUE iff OID is hardwired into device"), nmos::nc_object_constant_oid_property_id, nmos::fields::nc::constant_oid, U("NcBoolean"), true, false, false, false, value::null()); BST_REQUIRE_EQUAL(property_constant_oid, property_constant_oid_); const auto property_owner = value_of({ @@ -80,7 +80,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false }, { U("constraints"), value::null() } }); - const auto property_owner_ = nmos::details::make_nc_property_descriptor(U("OID of containing block. Can only ever be null for the root block"), nmos::nc_object_owner_property_id, nmos::fields::nc::owner, U("NcOid"), true, true, false, false, value::null()); + const auto property_owner_ = nmos::nc::details::make_nc_property_descriptor(U("OID of containing block. Can only ever be null for the root block"), nmos::nc_object_owner_property_id, nmos::fields::nc::owner, U("NcOid"), true, true, false, false, value::null()); BST_REQUIRE_EQUAL(property_owner, property_owner_); const auto property_role = value_of({ @@ -97,7 +97,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false }, { U("constraints"), value::null() } }); - const auto property_role_ = nmos::details::make_nc_property_descriptor(U("Role of object in the containing block"), nmos::nc_object_role_property_id, nmos::fields::nc::role, U("NcString"), true, false, false, false, value::null()); + const auto property_role_ = nmos::nc::details::make_nc_property_descriptor(U("Role of object in the containing block"), nmos::nc_object_role_property_id, nmos::fields::nc::role, U("NcString"), true, false, false, false, value::null()); BST_REQUIRE_EQUAL(property_role, property_role_); const auto property_user_label = value_of({ @@ -114,7 +114,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false }, { U("constraints"), value::null() } }); - const auto property_user_label_ = nmos::details::make_nc_property_descriptor(U("Scribble strip"), nmos::nc_object_user_label_property_id, nmos::fields::nc::user_label, U("NcString"), false, true, false, false, value::null()); + const auto property_user_label_ = nmos::nc::details::make_nc_property_descriptor(U("Scribble strip"), nmos::nc_object_user_label_property_id, nmos::fields::nc::user_label, U("NcString"), false, true, false, false, value::null()); BST_REQUIRE_EQUAL(property_user_label, property_user_label_); const auto property_touchpoints = value_of({ @@ -131,7 +131,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false }, { U("constraints"), value::null() } }); - const auto property_touchpoints_ = nmos::details::make_nc_property_descriptor(U("Touchpoints to other contexts"), nmos::nc_object_touchpoints_property_id, nmos::fields::nc::touchpoints, U("NcTouchpoint"), true, true, true, false, value::null()); + const auto property_touchpoints_ = nmos::nc::details::make_nc_property_descriptor(U("Touchpoints to other contexts"), nmos::nc_object_touchpoints_property_id, nmos::fields::nc::touchpoints, U("NcTouchpoint"), true, true, true, false, value::null()); BST_REQUIRE_EQUAL(property_touchpoints, property_touchpoints_); const auto property_runtime_property_constraints = value_of({ @@ -148,7 +148,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false }, { U("constraints"), value::null() } }); - const auto property_runtime_property_constraints_ = nmos::details::make_nc_property_descriptor(U("Runtime property constraints"), nmos::nc_object_runtime_property_constraints_property_id, nmos::fields::nc::runtime_property_constraints, U("NcPropertyConstraints"), true, true, true, false, value::null()); + const auto property_runtime_property_constraints_ = nmos::nc::details::make_nc_property_descriptor(U("Runtime property constraints"), nmos::nc_object_runtime_property_constraints_property_id, nmos::fields::nc::runtime_property_constraints, U("NcPropertyConstraints"), true, true, true, false, value::null()); BST_REQUIRE_EQUAL(property_runtime_property_constraints, property_runtime_property_constraints_); const auto method_get = value_of({ @@ -174,8 +174,8 @@ BST_TEST_CASE(testNcClassDescriptor) { auto parameters = value::array(); - web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - const auto method_get_ = nmos::details::make_nc_method_descriptor(U("Get property value"), nmos::nc_object_get_method_id, U("Get"), U("NcMethodResultPropertyValue"), parameters, false); + web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + const auto method_get_ = nmos::nc::details::make_nc_method_descriptor(U("Get property value"), nmos::nc_object_get_method_id, U("Get"), U("NcMethodResultPropertyValue"), parameters, false); BST_REQUIRE_EQUAL(method_get, method_get_); } @@ -211,9 +211,9 @@ BST_TEST_CASE(testNcClassDescriptor) { auto parameters = value::array(); - web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); - const auto method_set_ = nmos::details::make_nc_method_descriptor(U("Set property value"), nmos::nc_object_set_method_id, U("Set"), U("NcMethodResult"), parameters, false); + web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); + const auto method_set_ = nmos::nc::details::make_nc_method_descriptor(U("Set property value"), nmos::nc_object_set_method_id, U("Set"), U("NcMethodResult"), parameters, false); BST_REQUIRE_EQUAL(method_set, method_set_); } @@ -249,9 +249,9 @@ BST_TEST_CASE(testNcClassDescriptor) { auto parameters = value::array(); - web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); - const auto method_get_sequence_item_ = nmos::details::make_nc_method_descriptor(U("Get sequence item"), nmos::nc_object_get_sequence_item_method_id, U("GetSequenceItem"), U("NcMethodResultPropertyValue"), parameters, false); + web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + const auto method_get_sequence_item_ = nmos::nc::details::make_nc_method_descriptor(U("Get sequence item"), nmos::nc_object_get_sequence_item_method_id, U("GetSequenceItem"), U("NcMethodResultPropertyValue"), parameters, false); BST_REQUIRE_EQUAL(method_get_sequence_item, method_get_sequence_item_); } @@ -295,10 +295,10 @@ BST_TEST_CASE(testNcClassDescriptor) { auto parameters = value::array(); - web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); - web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); - const auto method_set_sequence_item_ = nmos::details::make_nc_method_descriptor(U("Set sequence item value"), nmos::nc_object_set_sequence_item_method_id, U("SetSequenceItem"), U("NcMethodResult"), parameters, false); + web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); + const auto method_set_sequence_item_ = nmos::nc::details::make_nc_method_descriptor(U("Set sequence item value"), nmos::nc_object_set_sequence_item_method_id, U("SetSequenceItem"), U("NcMethodResult"), parameters, false); BST_REQUIRE_EQUAL(method_set_sequence_item, method_set_sequence_item_); } @@ -334,9 +334,9 @@ BST_TEST_CASE(testNcClassDescriptor) { auto parameters = value::array(); - web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); - const auto method_add_sequence_item_ = nmos::details::make_nc_method_descriptor(U("Add item to sequence"), nmos::nc_object_add_sequence_item_method_id, U("AddSequenceItem"), U("NcMethodResultId"), parameters, false); + web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); + const auto method_add_sequence_item_ = nmos::nc::details::make_nc_method_descriptor(U("Add item to sequence"), nmos::nc_object_add_sequence_item_method_id, U("AddSequenceItem"), U("NcMethodResultId"), parameters, false); BST_REQUIRE_EQUAL(method_add_sequence_item, method_add_sequence_item_); } @@ -372,9 +372,9 @@ BST_TEST_CASE(testNcClassDescriptor) { auto parameters = value::array(); - web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); - const auto method_remove_sequence_item_ = nmos::details::make_nc_method_descriptor(U("Delete sequence item"), nmos::nc_object_remove_sequence_item_method_id, U("RemoveSequenceItem"), U("NcMethodResult"), parameters, false); + web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + const auto method_remove_sequence_item_ = nmos::nc::details::make_nc_method_descriptor(U("Delete sequence item"), nmos::nc_object_remove_sequence_item_method_id, U("RemoveSequenceItem"), U("NcMethodResult"), parameters, false); BST_REQUIRE_EQUAL(method_remove_sequence_item, method_remove_sequence_item_); } @@ -402,8 +402,8 @@ BST_TEST_CASE(testNcClassDescriptor) { auto parameters = value::array(); - web::json::push_back(parameters, nmos::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - const auto method_get_sequence_length_ = nmos::details::make_nc_method_descriptor(U("Get sequence length"), nmos::nc_object_get_sequence_length_method_id, U("GetSequenceLength"), U("NcMethodResultLength"), parameters, false); + web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + const auto method_get_sequence_length_ = nmos::nc::details::make_nc_method_descriptor(U("Get sequence length"), nmos::nc_object_get_sequence_length_method_id, U("GetSequenceLength"), U("NcMethodResultLength"), parameters, false); BST_REQUIRE_EQUAL(method_get_sequence_length, method_get_sequence_length_); } @@ -419,7 +419,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false } }); - const auto event_property_changed_ = nmos::details::make_nc_event_descriptor(U("Property changed event"), nmos::nc_object_property_changed_event_id, U("PropertyChanged"), U("NcPropertyChangedEventData"), false); + const auto event_property_changed_ = nmos::nc::details::make_nc_event_descriptor(U("Property changed event"), nmos::nc_object_property_changed_event_id, U("PropertyChanged"), U("NcPropertyChangedEventData"), false); BST_REQUIRE_EQUAL(event_property_changed, event_property_changed_); const auto nc_object_class = value_of({ @@ -452,7 +452,7 @@ BST_TEST_CASE(testNcClassDescriptor) event_property_changed }) } }); - const auto nc_object_class_ = nmos::details::make_nc_class_descriptor(U("NcObject class descriptor"), nmos::nc_object_class_id, U("NcObject"), nmos::make_nc_object_properties(), nmos::make_nc_object_methods(), nmos::make_nc_object_events()); + const auto nc_object_class_ = nmos::nc::details::make_nc_class_descriptor(U("NcObject class descriptor"), nmos::nc_object_class_id, U("NcObject"), nmos::nc::make_nc_object_properties(), nmos::nc::make_nc_object_methods(), nmos::nc::make_nc_object_events()); BST_REQUIRE_EQUAL(nc_object_class, nc_object_class_); } @@ -522,13 +522,13 @@ BST_TEST_CASE(testNcDatatypeDescriptorStruct) }); auto fields = value::array(); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Role of member in its containing block"), nmos::fields::nc::role, U("NcString"), false, false, value::null())); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("OID of member"), nmos::fields::nc::oid, U("NcOid"), false, false, value::null())); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("TRUE iff member's OID is hardwired into device"), nmos::fields::nc::constant_oid, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Class ID"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("User label"), nmos::fields::nc::user_label, U("NcString"), true, false, value::null())); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Containing block's OID"), nmos::fields::nc::owner, U("NcOid"), false, false, value::null())); - const auto nc_datatype_descriptor_ = nmos::details::make_nc_datatype_descriptor_struct(U("Descriptor which is specific to a block member"), U("NcBlockMemberDescriptor"), fields, U("NcDescriptor"), value::null()); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Role of member in its containing block"), nmos::fields::nc::role, U("NcString"), false, false, value::null())); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("OID of member"), nmos::fields::nc::oid, U("NcOid"), false, false, value::null())); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("TRUE iff member's OID is hardwired into device"), nmos::fields::nc::constant_oid, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Class ID"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("User label"), nmos::fields::nc::user_label, U("NcString"), true, false, value::null())); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Containing block's OID"), nmos::fields::nc::owner, U("NcOid"), false, false, value::null())); + const auto nc_datatype_descriptor_ = nmos::nc::details::make_nc_datatype_descriptor_struct(U("Descriptor which is specific to a block member"), U("NcBlockMemberDescriptor"), fields, U("NcDescriptor"), value::null()); BST_REQUIRE_EQUAL(nc_datatype_descriptor, nc_datatype_descriptor_); } @@ -548,7 +548,7 @@ BST_TEST_CASE(testNcDatatypeTypedef) { U("isSequence"), true }, { U("constraints"), value::null() } }); - const auto nc_class_id_ = nmos::details::make_nc_datatype_typedef(U("Sequence of class ID fields"), U("NcClassId"), true, U("NcInt32"), value::null()); + const auto nc_class_id_ = nmos::nc::details::make_nc_datatype_typedef(U("Sequence of class ID fields"), U("NcClassId"), true, U("NcInt32"), value::null()); BST_REQUIRE_EQUAL(nc_class_id, nc_class_id_); } @@ -600,13 +600,13 @@ BST_TEST_CASE(testNcDatatypeDescriptorEnum) }); auto items = value::array(); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Unknown"), U("Unknown"), 0)); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Normal operation"), U("NormalOperation"), 1)); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Device is initializing"), U("Initializing"), 2)); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Device is performing a software or firmware update"), U("Updating"), 3)); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Device is experiencing a licensing error"), U("LicensingError"), 4)); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("Device is experiencing an internal error"), U("InternalError"), 5)); - const auto nc_device_generic_state_ = nmos::details::make_nc_datatype_descriptor_enum(U("Device generic operational state"), U("NcDeviceGenericState"), items, value::null()); + web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Unknown"), U("Unknown"), 0)); + web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Normal operation"), U("NormalOperation"), 1)); + web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Device is initializing"), U("Initializing"), 2)); + web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Device is performing a software or firmware update"), U("Updating"), 3)); + web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Device is experiencing a licensing error"), U("LicensingError"), 4)); + web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Device is experiencing an internal error"), U("InternalError"), 5)); + const auto nc_device_generic_state_ = nmos::nc::details::make_nc_datatype_descriptor_enum(U("Device generic operational state"), U("NcDeviceGenericState"), items, value::null()); BST_REQUIRE_EQUAL(nc_device_generic_state, nc_device_generic_state_); } @@ -623,7 +623,7 @@ BST_TEST_CASE(testNcDatatypeDescriptorPrimitive) { U("constraints"), value::null() } }); - const auto test_primitive_ = nmos::details::make_nc_datatype_descriptor_primitive(U("Primitive datatype descriptor"), U("test_primitive"), value::null()); + const auto test_primitive_ = nmos::nc::details::make_nc_datatype_descriptor_primitive(U("Primitive datatype descriptor"), U("test_primitive"), value::null()); BST_REQUIRE_EQUAL(test_primitive, test_primitive_); } @@ -713,8 +713,8 @@ BST_TEST_CASE(testConstraints) // constraints // runtime constraints - const auto runtime_property_string_constraints = nmos::details::make_nc_property_constraints_string(property_string_id, 10, U("^[0-9]+$")); - const auto runtime_property_int32_constraints = nmos::details::make_nc_property_constraints_number(property_int32_id, 10, 1000, 1); + const auto runtime_property_string_constraints = nmos::nc::details::make_nc_property_constraints_string(property_string_id, 10, U("^[0-9]+$")); + const auto runtime_property_int32_constraints = nmos::nc::details::make_nc_property_constraints_number(property_int32_id, 10, 1000, 1); const auto runtime_property_constraints = value_of({ { runtime_property_string_constraints }, @@ -722,65 +722,65 @@ BST_TEST_CASE(testConstraints) }); // property constraints - const auto property_string_constraints = nmos::details::make_nc_parameter_constraints_string(5, U("^[a-z]+$")); - const auto property_int32_constraints = nmos::details::make_nc_parameter_constraints_number(50, 500, 5); + const auto property_string_constraints = nmos::nc::details::make_nc_parameter_constraints_string(5, U("^[a-z]+$")); + const auto property_int32_constraints = nmos::nc::details::make_nc_parameter_constraints_number(50, 500, 5); // datatype constraints - const auto datatype_string_constraints = nmos::details::make_nc_parameter_constraints_string(2, U("^[0-9a-z]+$")); - const auto datatype_int32_constraints = nmos::details::make_nc_parameter_constraints_number(100, 250, 10); + const auto datatype_string_constraints = nmos::nc::details::make_nc_parameter_constraints_string(2, U("^[0-9a-z]+$")); + const auto datatype_int32_constraints = nmos::nc::details::make_nc_parameter_constraints_number(100, 250, 10); // datatypes - const auto no_constraints_bool_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints boolean datatype"), U("NoConstraintsBoolean"), false, U("NcBoolean"), value::null()); - const auto no_constraints_int16_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints int16 datatype"), U("NoConstraintsInt16"), false, U("NcInt16"), value::null()); - const auto no_constraints_int32_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints int32 datatype"), U("NoConstraintsInt32"), false, U("NcInt32"), value::null()); - const auto no_constraints_int64_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints int64 datatype"), U("NoConstraintsInt64"), false, U("NcInt64"), value::null()); - const auto no_constraints_uint16_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints uint16 datatype"), U("NoConstraintsUint16"), false, U("NcUint16"), value::null()); - const auto no_constraints_uint32_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints uint32 datatype"), U("NoConstraintsUint32"), false, U("NcUint32"), value::null()); - const auto no_constraints_uint64_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints uint64 datatype"), U("NoConstraintsUint64"), false, U("NcUint64"), value::null()); - const auto no_constraints_float32_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints float32 datatype"), U("NoConstraintsFloat32"), false, U("NcFloat32"), value::null()); - const auto no_constraints_float64_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints float64 datatype"), U("NoConstraintsFloat64"), false, U("NcFloat64"), value::null()); - const auto no_constraints_string_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints string datatype"), U("NoConstraintsString"), false, U("NcString"), value::null()); - const auto with_constraints_string_datatype = nmos::details::make_nc_datatype_typedef(U("With constraints string datatype"), U("WithConstraintsString"), false, U("NcString"), datatype_string_constraints); - const auto with_constraints_int32_datatype = nmos::details::make_nc_datatype_typedef(U("With constraints int32 datatype"), U("WithConstraintsInt32"), false, U("NcInt32"), datatype_int32_constraints); - const auto no_constraints_int32_seq_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints int64 datatype"), U("NoConstraintsInt64"), true, U("NcInt32"), value::null()); - const auto no_constraints_string_seq_datatype = nmos::details::make_nc_datatype_typedef(U("No constraints string datatype"), U("NoConstraintsString"), true, U("NcString"), value::null()); + const auto no_constraints_bool_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints boolean datatype"), U("NoConstraintsBoolean"), false, U("NcBoolean"), value::null()); + const auto no_constraints_int16_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints int16 datatype"), U("NoConstraintsInt16"), false, U("NcInt16"), value::null()); + const auto no_constraints_int32_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints int32 datatype"), U("NoConstraintsInt32"), false, U("NcInt32"), value::null()); + const auto no_constraints_int64_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints int64 datatype"), U("NoConstraintsInt64"), false, U("NcInt64"), value::null()); + const auto no_constraints_uint16_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints uint16 datatype"), U("NoConstraintsUint16"), false, U("NcUint16"), value::null()); + const auto no_constraints_uint32_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints uint32 datatype"), U("NoConstraintsUint32"), false, U("NcUint32"), value::null()); + const auto no_constraints_uint64_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints uint64 datatype"), U("NoConstraintsUint64"), false, U("NcUint64"), value::null()); + const auto no_constraints_float32_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints float32 datatype"), U("NoConstraintsFloat32"), false, U("NcFloat32"), value::null()); + const auto no_constraints_float64_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints float64 datatype"), U("NoConstraintsFloat64"), false, U("NcFloat64"), value::null()); + const auto no_constraints_string_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints string datatype"), U("NoConstraintsString"), false, U("NcString"), value::null()); + const auto with_constraints_string_datatype = nmos::nc::details::make_nc_datatype_typedef(U("With constraints string datatype"), U("WithConstraintsString"), false, U("NcString"), datatype_string_constraints); + const auto with_constraints_int32_datatype = nmos::nc::details::make_nc_datatype_typedef(U("With constraints int32 datatype"), U("WithConstraintsInt32"), false, U("NcInt32"), datatype_int32_constraints); + const auto no_constraints_int32_seq_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints int64 datatype"), U("NoConstraintsInt64"), true, U("NcInt32"), value::null()); + const auto no_constraints_string_seq_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints string datatype"), U("NoConstraintsString"), true, U("NcString"), value::null()); enum enum_value { foo, bar, baz }; auto items = value::array(); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("foo"), U("foo"), enum_value::foo)); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("bar"), U("bar"), enum_value::bar)); - web::json::push_back(items, nmos::details::make_nc_enum_item_descriptor(U("baz"), U("baz"), enum_value::baz)); - const auto enum_datatype = nmos::details::make_nc_datatype_descriptor_enum(U("enum datatype"), U("enumDatatype"), items, value::null()); // no datatype constraints for enum datatype + web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("foo"), U("foo"), enum_value::foo)); + web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("bar"), U("bar"), enum_value::bar)); + web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("baz"), U("baz"), enum_value::baz)); + const auto enum_datatype = nmos::nc::details::make_nc_datatype_descriptor_enum(U("enum datatype"), U("enumDatatype"), items, value::null()); // no datatype constraints for enum datatype auto simple_struct_fields = value::array(); - web::json::push_back(simple_struct_fields, nmos::details::make_nc_field_descriptor(U("simple enum property example"), U("simpleEnumProperty"), U("enumDatatype"), false, false, value::null())); // no field constraints for enum field, as it is already described by its type - web::json::push_back(simple_struct_fields, nmos::details::make_nc_field_descriptor(U("simple string property example"), U("simpleStringProperty"), U("NcString"), false, false, datatype_string_constraints)); - web::json::push_back(simple_struct_fields, nmos::details::make_nc_field_descriptor(U("simple number property example"), U("simpleNumberProperty"), U("NcInt32"), false, false, datatype_int32_constraints)); - web::json::push_back(simple_struct_fields, nmos::details::make_nc_field_descriptor(U("simle boolean property example"), U("simpleBooleanProperty"), U("NcBoolean"), false, false, value::null())); // no field constraints for boolean field, as it is already described by its type - const auto simple_struct_datatype = nmos::details::make_nc_datatype_descriptor_struct(U("simple struct datatype"), U("simpleStructDatatype"), simple_struct_fields, value::null()); // no datatype constraints for struct datatype + web::json::push_back(simple_struct_fields, nmos::nc::details::make_nc_field_descriptor(U("simple enum property example"), U("simpleEnumProperty"), U("enumDatatype"), false, false, value::null())); // no field constraints for enum field, as it is already described by its type + web::json::push_back(simple_struct_fields, nmos::nc::details::make_nc_field_descriptor(U("simple string property example"), U("simpleStringProperty"), U("NcString"), false, false, datatype_string_constraints)); + web::json::push_back(simple_struct_fields, nmos::nc::details::make_nc_field_descriptor(U("simple number property example"), U("simpleNumberProperty"), U("NcInt32"), false, false, datatype_int32_constraints)); + web::json::push_back(simple_struct_fields, nmos::nc::details::make_nc_field_descriptor(U("simle boolean property example"), U("simpleBooleanProperty"), U("NcBoolean"), false, false, value::null())); // no field constraints for boolean field, as it is already described by its type + const auto simple_struct_datatype = nmos::nc::details::make_nc_datatype_descriptor_struct(U("simple struct datatype"), U("simpleStructDatatype"), simple_struct_fields, value::null()); // no datatype constraints for struct datatype auto fields = value::array(); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Enum property example"), U("enumProperty"), U("enumDatatype"), false, false, value::null())); // no field constraints for enum field, as it is already described by its type - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("String property example"), U("stringProperty"), U("NcString"), false, false, datatype_string_constraints)); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Number property example"), U("numberProperty"), U("NcInt32"), false, false, datatype_int32_constraints)); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Boolean property example"), U("booleanProperty"), U("NcBoolean"), false, false, value::null())); // no field constraints for boolean field, as it is already described by its type - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Struct property example"), U("structProperty"), U("simpleStructDatatype"), false, false, value::null())); // no datatype constraints for struct datatype - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Sequence enum property example"), U("sequenceEnumProperty"), U("enumDatatype"), false, true, value::null())); // no field constraints for enum field, as it is already described by its type - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Sequence string property example"), U("sequenceStringProperty"), U("NcString"), false, true, datatype_string_constraints)); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Sequence number property example"), U("sequenceNumberProperty"), U("NcInt32"), false, true, datatype_int32_constraints)); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Sequence boolean property example"), U("sequenceBooleanProperty"), U("NcBoolean"), false, true, value::null())); // no field constraints for boolean field, as it is already described by its type - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Sequence struct property example"), U("sequenceStructProperty"), U("simpleStructDatatype"), false, true, value::null())); // no field constraints for struct field - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Nullable Enum property example"), U("enumPropertyNullable"), U("enumDatatype"), true, false, value::null())); // no field constraints for enum field, as it is already described by its type - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Nullable String property example"), U("stringPropertyNullable"), U("NcString"), true, false, datatype_string_constraints)); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Nullable Number property example"), U("numberPropertyNullable"), U("NcInt32"), true, false, datatype_int32_constraints)); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Nullable Boolean property example"), U("booleanPropertyNullable"), U("NcBoolean"), true, false, value::null())); // no field constraints for boolean field, as it is already described by its type - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Nullable Struct property example"), U("structPropertyNullable"), U("simpleStructDatatype"), true, false, value::null())); // no datatype constraints for struct datatype - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Nullable Sequence enum property example"), U("sequenceEnumPropertyNullable"), U("enumDatatype"), true, true, value::null())); // no field constraints for enum field, as it is already described by its type - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Nullable Sequence string property example"), U("sequenceStringPropertyNullable"), U("NcString"), true, true, datatype_string_constraints)); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Nullable Sequence number property example"), U("sequenceNumberPropertyNullable"), U("NcInt32"), true, true, datatype_int32_constraints)); - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Nullable Sequence boolean property example"), U("sequenceBooleanPropertyNullable"), U("NcBoolean"), true, true, value::null())); // no field constraints for boolean field, as it is already described by its type - web::json::push_back(fields, nmos::details::make_nc_field_descriptor(U("Nullable Sequence struct property example"), U("sequenceStructPropertyNullable"), U("simpleStructDatatype"), true, true, value::null())); // no field constraints for struct field - const auto struct_datatype = nmos::details::make_nc_datatype_descriptor_struct(U("struct datatype"), U("structDatatype"), fields, value::null()); // no datatype constraints for struct datatype + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Enum property example"), U("enumProperty"), U("enumDatatype"), false, false, value::null())); // no field constraints for enum field, as it is already described by its type + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("String property example"), U("stringProperty"), U("NcString"), false, false, datatype_string_constraints)); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Number property example"), U("numberProperty"), U("NcInt32"), false, false, datatype_int32_constraints)); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Boolean property example"), U("booleanProperty"), U("NcBoolean"), false, false, value::null())); // no field constraints for boolean field, as it is already described by its type + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Struct property example"), U("structProperty"), U("simpleStructDatatype"), false, false, value::null())); // no datatype constraints for struct datatype + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Sequence enum property example"), U("sequenceEnumProperty"), U("enumDatatype"), false, true, value::null())); // no field constraints for enum field, as it is already described by its type + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Sequence string property example"), U("sequenceStringProperty"), U("NcString"), false, true, datatype_string_constraints)); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Sequence number property example"), U("sequenceNumberProperty"), U("NcInt32"), false, true, datatype_int32_constraints)); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Sequence boolean property example"), U("sequenceBooleanProperty"), U("NcBoolean"), false, true, value::null())); // no field constraints for boolean field, as it is already described by its type + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Sequence struct property example"), U("sequenceStructProperty"), U("simpleStructDatatype"), false, true, value::null())); // no field constraints for struct field + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Enum property example"), U("enumPropertyNullable"), U("enumDatatype"), true, false, value::null())); // no field constraints for enum field, as it is already described by its type + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable String property example"), U("stringPropertyNullable"), U("NcString"), true, false, datatype_string_constraints)); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Number property example"), U("numberPropertyNullable"), U("NcInt32"), true, false, datatype_int32_constraints)); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Boolean property example"), U("booleanPropertyNullable"), U("NcBoolean"), true, false, value::null())); // no field constraints for boolean field, as it is already described by its type + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Struct property example"), U("structPropertyNullable"), U("simpleStructDatatype"), true, false, value::null())); // no datatype constraints for struct datatype + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Sequence enum property example"), U("sequenceEnumPropertyNullable"), U("enumDatatype"), true, true, value::null())); // no field constraints for enum field, as it is already described by its type + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Sequence string property example"), U("sequenceStringPropertyNullable"), U("NcString"), true, true, datatype_string_constraints)); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Sequence number property example"), U("sequenceNumberPropertyNullable"), U("NcInt32"), true, true, datatype_int32_constraints)); + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Sequence boolean property example"), U("sequenceBooleanPropertyNullable"), U("NcBoolean"), true, true, value::null())); // no field constraints for boolean field, as it is already described by its type + web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Sequence struct property example"), U("sequenceStructPropertyNullable"), U("simpleStructDatatype"), true, true, value::null())); // no field constraints for struct field + const auto struct_datatype = nmos::nc::details::make_nc_datatype_descriptor_struct(U("struct datatype"), U("structDatatype"), fields, value::null()); // no datatype constraints for struct datatype // setup datatypes in control_protocol_state nmos::experimental::control_protocol_state control_protocol_state; diff --git a/Development/nmos/test/control_protocol_utils_test.cpp b/Development/nmos/test/control_protocol_utils_test.cpp index 8687db947..ff9938095 100644 --- a/Development/nmos/test/control_protocol_utils_test.cpp +++ b/Development/nmos/test/control_protocol_utils_test.cpp @@ -328,7 +328,7 @@ BST_TEST_CASE(testActivateDeactivateReceiverMonitor) // check that the property changed handler gets called reset_monitor_called = true; - return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }); + return nmos::nc::details::make_nc_method_result({ nmos::nc_method_status::ok }); }; nmos::experimental::control_protocol_state control_protocol_state(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, reset_monitor); @@ -690,7 +690,7 @@ BST_TEST_CASE(testActivateDeactivateSenderMonitor) // check that the property changed handler gets called reset_monitor_called = true; - return nmos::details::make_nc_method_result({ nmos::nc_method_status::ok }); + return nmos::nc::details::make_nc_method_result({ nmos::nc_method_status::ok }); }; nmos::experimental::control_protocol_state control_protocol_state(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, reset_monitor); @@ -830,7 +830,7 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) // check that the property changed handler gets called reset_monitor_called = true; - return nmos::details::make_nc_method_result({nmos::nc_method_status::ok}); + return nmos::nc::details::make_nc_method_result({nmos::nc_method_status::ok}); }; nmos::monitor_status_pending_handler monitor_status_pending = [&]() @@ -1148,9 +1148,9 @@ BST_TEST_CASE(testFindTouchpointResources) // Create Device Model auto oid = nmos::root_block_oid; - auto monitor1 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint1_id})} })); - auto monitor2 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint2_id})} })); - auto monitor3 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon3"), U("monitor 3"), U("monitor 3"), value_of({ {nmos::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, non_existant_id})} })); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint1_id})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint2_id})} })); + auto monitor3 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon3"), U("monitor 3"), U("monitor 3"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, non_existant_id})} })); nmos::resources resources; // Insert dummy NMOS resources From e58321f9c31959f632eb7ce5b7872ef0be32dfd7 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 12 Sep 2025 14:28:16 +0100 Subject: [PATCH 244/250] Remove nc from function names in the nc namespace --- .../nmos-cpp-node/node_implementation.cpp | 64 +- Development/nmos/configuration_api.cpp | 72 +- Development/nmos/configuration_methods.cpp | 20 +- Development/nmos/configuration_resources.cpp | 8 +- Development/nmos/configuration_utils.cpp | 76 +- .../nmos/control_protocol_behaviour.cpp | 4 +- .../nmos/control_protocol_handlers.cpp | 4 +- Development/nmos/control_protocol_methods.cpp | 146 +- .../nmos/control_protocol_resource.cpp | 1436 ++++++++--------- Development/nmos/control_protocol_resource.h | 392 ++--- .../nmos/control_protocol_resources.cpp | 18 +- Development/nmos/control_protocol_state.cpp | 274 ++-- Development/nmos/control_protocol_utils.cpp | 32 +- Development/nmos/control_protocol_ws_api.cpp | 10 +- .../nmos/test/configuration_methods_test.cpp | 6 +- .../nmos/test/configuration_utils_test.cpp | 390 ++--- .../test/control_protocol_methods_test.cpp | 2 +- .../nmos/test/control_protocol_test.cpp | 192 +-- .../nmos/test/control_protocol_utils_test.cpp | 12 +- 19 files changed, 1579 insertions(+), 1579 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 534ceb424..c27b0c037 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -960,7 +960,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // helper function to create Gain control instance auto make_gain_control = [&gain_value, &gain_control_class_id](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, float gain) { - auto data = nmos::nc::details::make_nc_worker(gain_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); + auto data = nmos::nc::details::make_worker(gain_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); data[gain_value] = value::number(gain); return nmos::control_protocol_resource{ nmos::is12_versions::v1_0, nmos::types::nc_worker, std::move(data), true }; @@ -996,17 +996,17 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr }; { // following constraints are used for the example control class level 0 datatype, level 1 property constraints and the method parameters constraints - auto make_string_example_argument_constraints = []() {return nmos::nc::details::make_nc_parameter_constraints_string(10, U("^[a-z]+$")); }; - auto make_number_example_argument_constraints = []() {return nmos::nc::details::make_nc_parameter_constraints_number(0, 1000, 1); }; + auto make_string_example_argument_constraints = []() {return nmos::nc::details::make_parameter_constraints_string(10, U("^[a-z]+$")); }; + auto make_number_example_argument_constraints = []() {return nmos::nc::details::make_parameter_constraints_number(0, 1000, 1); }; // Example control class property descriptors std::vector example_control_property_descriptors = { nmos::experimental::make_control_class_property_descriptor(U("Example enum property"), { 3, 1 }, enum_property, U("ExampleEnum")), // create "Example string property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use nmos::nc::details::make_nc_parameter_constraints_string to create property constraints + // use nmos::nc::details::make_parameter_constraints_string to create property constraints nmos::experimental::make_control_class_property_descriptor(U("Example string property"), { 3, 2 }, string_property, U("NcString"), false, false, false, false, make_string_example_argument_constraints()), // create "Example numeric property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use nmos::nc::details::make_nc_parameter_constraints_number to create property constraints + // use nmos::nc::details::make_parameter_constraints_number to create property constraints nmos::experimental::make_control_class_property_descriptor(U("Example numeric property"), { 3, 3 }, number_property, U("NcUint64"), false, false, false, false, make_number_example_argument_constraints()), nmos::experimental::make_control_class_property_descriptor(U("Example deprecated numeric property"), { 3, 4 }, deprecated_number_property, U("NcUint64"), false, false, false, true, make_number_example_argument_constraints()), nmos::experimental::make_control_class_property_descriptor(U("Example boolean property"), { 3, 5 }, boolean_property, U("NcBoolean")), @@ -1015,12 +1015,12 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr nmos::experimental::make_control_class_property_descriptor(U("Example method simple args invoke counter"), { 3, 8 }, method_simple_args_count, U("NcUint64"), true), nmos::experimental::make_control_class_property_descriptor(U("Example method obj arg invoke counter"), { 3, 9 }, method_object_arg_count, U("NcUint64"), true), // create "Example sequence string property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use nmos::nc::details::make_nc_parameter_constraints_string to create sequence property constraints + // use nmos::nc::details::make_parameter_constraints_string to create sequence property constraints nmos::experimental::make_control_class_property_descriptor(U("Example string sequence property"), { 3, 10 }, string_sequence, U("NcString"), false, false, true, false, make_string_example_argument_constraints()), nmos::experimental::make_control_class_property_descriptor(U("Example boolean sequence property"), { 3, 11 }, boolean_sequence, U("NcBoolean"), false, false, true), nmos::experimental::make_control_class_property_descriptor(U("Example enum sequence property"), { 3, 12 }, enum_sequence, U("ExampleEnum"), false, false, true), // create "Example sequence numeric property" with level 1: property constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use nmos::nc::details::make_nc_parameter_constraints_number to create sequence property constraints + // use nmos::nc::details::make_parameter_constraints_number to create sequence property constraints nmos::experimental::make_control_class_property_descriptor(U("Example number sequence property"), { 3, 13 }, number_sequence, U("NcUint64"), false, false, true, false, make_number_example_argument_constraints()), nmos::experimental::make_control_class_property_descriptor(U("Example object sequence property"), { 3, 14 }, object_sequence, U("ExampleDataType"), false, false, true) }; @@ -1031,7 +1031,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr slog::log(gate, SLOG_FLF) << "Executing the example method with no arguments"; - return nmos::nc::details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); + return nmos::nc::details::make_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); }; auto example_method_with_simple_args = [](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { @@ -1040,7 +1040,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr slog::log(gate, SLOG_FLF) << "Executing the example method with simple arguments: " << arguments.serialize(); - return nmos::nc::details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); + return nmos::nc::details::make_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); }; auto example_method_with_object_args = [](nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, slog::base_gate& gate) { @@ -1049,7 +1049,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr slog::log(gate, SLOG_FLF) << "Executing the example method with object argument: " << arguments.serialize(); - return nmos::nc::details::make_nc_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); + return nmos::nc::details::make_method_result({ is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::nc_method_status::ok }); }; // Example control class method descriptors std::vector example_control_method_descriptors = @@ -1085,32 +1085,32 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr using web::json::value; auto items = value::array(); - web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Undefined"), U("Undefined"), example_enum::Undefined)); - web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Alpha"), U("Alpha"), example_enum::Alpha)); - web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Beta"), U("Beta"), example_enum::Beta)); - web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Gamma"), U("Gamma"), example_enum::Gamma)); - return nmos::nc::details::make_nc_datatype_descriptor_enum(U("Example enum datatype"), U("ExampleEnum"), items, value::null()); + web::json::push_back(items, nmos::nc::details::make_enum_item_descriptor(U("Undefined"), U("Undefined"), example_enum::Undefined)); + web::json::push_back(items, nmos::nc::details::make_enum_item_descriptor(U("Alpha"), U("Alpha"), example_enum::Alpha)); + web::json::push_back(items, nmos::nc::details::make_enum_item_descriptor(U("Beta"), U("Beta"), example_enum::Beta)); + web::json::push_back(items, nmos::nc::details::make_enum_item_descriptor(U("Gamma"), U("Gamma"), example_enum::Gamma)); + return nmos::nc::details::make_datatype_descriptor_enum(U("Example enum datatype"), U("ExampleEnum"), items, value::null()); }; auto make_example_datatype_datatype = [&]() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Enum property example"), enum_property, U("ExampleEnum"), false, false, value::null())); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Enum property example"), enum_property, U("ExampleEnum"), false, false, value::null())); { // level 0: datatype constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use nmos::nc::details::make_nc_parameter_constraints_string to create datatype constraints + // use nmos::nc::details::make_parameter_constraints_string to create datatype constraints value datatype_constraints = make_string_example_argument_constraints(); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("String property example"), string_property, U("NcString"), false, false, datatype_constraints)); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("String property example"), string_property, U("NcString"), false, false, datatype_constraints)); } { // level 0: datatype constraints, See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use nmos::nc::details::make_nc_parameter_constraints_number to create datatype constraints + // use nmos::nc::details::make_parameter_constraints_number to create datatype constraints value datatype_constraints = make_number_example_argument_constraints(); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Number property example"), number_property, U("NcUint64"), false, false, datatype_constraints)); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Number property example"), number_property, U("NcUint64"), false, false, datatype_constraints)); } - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Boolean property example"), boolean_property, U("NcBoolean"), false, false, value::null())); - return nmos::nc::details::make_nc_datatype_descriptor_struct(U("Example data type"), U("ExampleDataType"), fields, value::null()); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Boolean property example"), boolean_property, U("NcBoolean"), false, false, value::null())); + return nmos::nc::details::make_datatype_descriptor_struct(U("Example data type"), U("ExampleDataType"), fields, value::null()); }; control_protocol_state.insert(nmos::experimental::datatype_descriptor{ make_example_enum_datatype() }); control_protocol_state.insert(nmos::experimental::datatype_descriptor{ make_example_datatype_datatype() }); @@ -1130,7 +1130,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // helper function to create Example control instance auto make_example_control = [&](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const value& touchpoints, const value& runtime_property_constraints, // level 2: runtime constraints. See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use of make_nc_property_constraints_string and make_nc_property_constraints_number to create runtime constraints + // use of make_property_constraints_string and make_property_constraints_number to create runtime constraints example_enum enum_property_, const utility::string_t& string_property_, uint64_t number_property_, @@ -1146,7 +1146,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr std::vector number_sequence_, std::vector object_sequence_) { - auto data = nmos::nc::details::make_nc_worker(example_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); + auto data = nmos::nc::details::make_worker(example_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); data[enum_property] = value::number(enum_property_); data[string_property] = value::string(string_property_); data[number_property] = value::number(number_property_); @@ -1205,7 +1205,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // helper function to create Temperature Sensor control instance auto make_temperature_sensor = [&temperature, &unit, temperature_sensor_control_class_id](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, float temperature_, const utility::string_t& unit_) { - auto data = nmos::nc::details::make_nc_worker(temperature_sensor_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); + auto data = nmos::nc::details::make_worker(temperature_sensor_control_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true); data[temperature] = value::number(temperature_); data[unit] = value::string(unit_); @@ -1251,10 +1251,10 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr auto example_control = make_example_control(++oid, nmos::root_block_oid, U("ExampleControl"), U("Example control worker"), U("Example control worker"), value::null(), // specify the level 2: runtime constraints, see https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Constraints.html - // use of make_nc_property_constraints_string and make_nc_property_constraints_number to create runtime constraints + // use of make_property_constraints_string and make_property_constraints_number to create runtime constraints value_of({ - { nmos::nc::details::make_nc_property_constraints_string({3, 2}, 5, U("^[a-z]+$")) }, - { nmos::nc::details::make_nc_property_constraints_number({3, 3}, 10, 100, 2) } + { nmos::nc::details::make_property_constraints_string({3, 2}, 5, U("^[a-z]+$")) }, + { nmos::nc::details::make_property_constraints_number({3, 3}, 10, 100, 2) } }), example_enum::Undefined, U("test"), @@ -1294,7 +1294,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr utility::ostringstream_t role; role << U("receiver-monitor-") << ++count; const auto& receiver = nmos::find_resource(model.node_resources, receiver_id); - auto receiver_monitor = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, receiver_id}) } })); + auto receiver_monitor = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, receiver_id}) } })); // optionally indicate dependencies within the device model nmos::set_object_dependency_paths(receiver_monitor, {{U("root"), U("receivers")}}); // add receiver-monitor to root-block @@ -1315,7 +1315,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr utility::ostringstream_t role; role << U("sender-monitor-") << ++count; const auto& sender = nmos::find_resource(model.node_resources, sender_id); - const auto sender_monitor = nmos::make_sender_monitor(++oid, true, nmos::root_block_oid, role.str(), nmos::fields::label(sender->data), nmos::fields::description(sender->data), value_of({ { nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::sender, sender_id}) } })); + const auto sender_monitor = nmos::make_sender_monitor(++oid, true, nmos::root_block_oid, role.str(), nmos::fields::label(sender->data), nmos::fields::description(sender->data), value_of({ { nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::sender, sender_id}) } })); // add sender-monitor to root-block nmos::nc::push_back(root_block, sender_monitor); @@ -1443,7 +1443,7 @@ void node_implementation_run(nmos::node_model& model, nmos::experimental::contro auto found = nmos::find_resource_if(resources, nmos::types::nc_worker, [&temperature_sensor_control_class_id](const nmos::resource& resource) { - return temperature_sensor_control_class_id == nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + return temperature_sensor_control_class_id == nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(resource.data)); }); if (resources.end() != found) @@ -2056,7 +2056,7 @@ nmos::create_device_model_object_handler make_create_device_model_object_handler const auto& touchpoint_uuid = nmos::fields::nc::id(nmos::fields::nc::resource(*touchpoints.as_array().begin())); // In the case of validate = true, the object created will not be added to the device model, but it's values will be checked against the backup dataset - return nmos::make_receiver_monitor(oid, true, owner, role, user_label, U(""), web::json::value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})} })); + return nmos::make_receiver_monitor(oid, true, owner, role, user_label, U(""), web::json::value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint_uuid.as_string()})} })); }; } diff --git a/Development/nmos/configuration_api.cpp b/Development/nmos/configuration_api.cpp index 499e044dc..90f30635a 100644 --- a/Development/nmos/configuration_api.cpp +++ b/Development/nmos/configuration_api.cpp @@ -70,7 +70,7 @@ namespace nmos role_paths.insert(role_path + U("/")); // get members on all NcBlock(s) - if (nmos::nc::is_block(nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + if (nmos::nc::is_block(nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(member)))) { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(member); @@ -216,7 +216,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -236,7 +236,7 @@ namespace nmos { std::set properties_routes; - auto class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); + auto class_id = nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(resource->data)); while (!class_id.empty()) { const auto& control_class = get_control_protocol_class_descriptor(class_id); @@ -257,7 +257,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -277,7 +277,7 @@ namespace nmos { std::set methods_routes; - auto class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); + auto class_id = nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(resource->data)); while (!class_id.empty()) { const auto& control_class = get_control_protocol_class_descriptor(class_id); @@ -305,7 +305,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -323,7 +323,7 @@ namespace nmos const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { - nc_class_id class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); + nc_class_id class_id = nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(resource->data)); if (!class_id.empty()) { @@ -353,17 +353,17 @@ namespace nmos } auto class_descriptor = fixed_role.is_null() - ? nc::details::make_nc_class_descriptor(description, class_id, name, property_descriptors, method_descriptors, event_descriptors) - : nc::details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), property_descriptors, method_descriptors, event_descriptors); + ? nc::details::make_class_descriptor(description, class_id, name, property_descriptors, method_descriptors, event_descriptors) + : nc::details::make_class_descriptor(description, class_id, name, fixed_role.as_string(), property_descriptors, method_descriptors, event_descriptors); - auto method_result = nc::details::make_nc_method_result({ nmos::nc_method_status::ok }, class_descriptor); + auto method_result = nc::details::make_method_result({ nmos::nc_method_status::ok }, class_descriptor); set_reply(res, status_codes::OK, method_result); } } else { // resource not found for the role path - auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -383,11 +383,11 @@ namespace nmos if (resources.end() != resource) { // find the relevant nc_property_descriptor - const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); + const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), nc::details::parse_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { // resource not found for the role path - auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::property_not_implemented }, U("Not Found; ") + property_id); + auto method_result = nc::details::make_method_result_error({ nmos::nc_method_status::property_not_implemented }, U("Not Found; ") + property_id); set_reply(res, status_codes::NotFound, method_result); } else @@ -398,7 +398,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -418,7 +418,7 @@ namespace nmos if (resources.end() != resource) { // find the relevant nc_property_descriptor - const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); + const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), nc::details::parse_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); const auto& property_type = nmos::fields::nc::type_name(property_descriptor); auto datatype_descriptor = nc::details::get_datatype_descriptor(value::string(property_type), get_control_protocol_datatype_descriptor); @@ -442,19 +442,19 @@ namespace nmos if (property_descriptor.is_null()) { // property not found - auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::property_not_implemented }, U("Not Found; ") + property_id); + auto method_result = nc::details::make_method_result_error({ nmos::nc_method_status::property_not_implemented }, U("Not Found; ") + property_id); set_reply(res, status_codes::NotFound, method_result); } else { - auto method_result = nc::details::make_nc_method_result({ nmos::nc_method_status::ok }, datatype_descriptor); + auto method_result = nc::details::make_method_result({ nmos::nc_method_status::ok }, datatype_descriptor); set_reply(res, status_codes::OK, method_result); } } else { // resource not found for the role path - auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -474,7 +474,7 @@ namespace nmos if (resources.end() != resource) { auto arguments = value_of({ - { nmos::fields::nc::id, nc::details::make_nc_property_id(details::parse_formatted_property_id(property_id))}, + { nmos::fields::nc::id, nc::details::make_property_id(details::parse_formatted_property_id(property_id))}, }); auto result = nc::get(*resource, arguments, false, get_control_protocol_class_descriptor, gate_); @@ -485,7 +485,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -514,7 +514,7 @@ namespace nmos const auto& resource = nc::find_resource_by_role_path(resources, role_path); if (resources.end() != resource) { - auto method = get_control_protocol_method_descriptor(nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), details::parse_formatted_method_id(method_id)); + auto method = get_control_protocol_method_descriptor(nc::details::parse_class_id(nmos::fields::nc::class_id(resource->data)), details::parse_formatted_method_id(method_id)); auto& nc_method_descriptor = method.first; auto& control_method_handler = method.second; web::http::status_code code{ status_codes::BadRequest }; @@ -541,7 +541,7 @@ namespace nmos // invalid arguments utility::stringstream_t ss; ss << U("invalid argument: ") << arguments.serialize() << " error: " << e.what(); - method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + method_result = nc::details::make_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); code = status_codes::BadRequest; } @@ -552,7 +552,7 @@ namespace nmos utility::stringstream_t ss; ss << U("unsupported method_id: ") << method_id << U(" for control class class_id: ") << resource->data.at(nmos::fields::nc::class_id).serialize(); - method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, ss.str()); + method_result = nc::details::make_method_result_error({ nmos::nc_method_status::method_not_implemented }, ss.str()); code = status_codes::NotFound; } @@ -561,7 +561,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -591,17 +591,17 @@ namespace nmos if (resources.end() != resource) { // find the relevant nc_property_descriptor - const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); + const auto& property_descriptor = nc::find_property_descriptor(details::parse_formatted_property_id(property_id), nc::details::parse_class_id(nmos::fields::nc::class_id(resource->data)), get_control_protocol_class_descriptor); if (property_descriptor.is_null()) { // property not found - auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + property_id); + auto method_result = nc::details::make_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + property_id); set_reply(res, status_codes::NotFound, method_result); } else { auto arguments = value_of({ - { nmos::fields::nc::id, nc::details::make_nc_property_id(details::parse_formatted_property_id(property_id))}, + { nmos::fields::nc::id, nc::details::make_property_id(details::parse_formatted_property_id(property_id))}, { nmos::fields::nc::value, nmos::fields::nc::value(body)} }); @@ -616,7 +616,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -656,7 +656,7 @@ namespace nmos // invalid arguments utility::stringstream_t ss; ss << U("parameter error: ") << e.what(); - method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + method_result = nc::details::make_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); code = status_codes::BadRequest; } @@ -665,7 +665,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } @@ -711,7 +711,7 @@ namespace nmos // invalid arguments utility::stringstream_t ss; ss << U("parameter error: ") << e.what(); - method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + method_result = nc::details::make_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); code = status_codes::BadRequest; } @@ -720,7 +720,7 @@ namespace nmos // JSON validation error utility::stringstream_t ss; ss << U("JSON validation error: ") << e.what(); - method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + method_result = nc::details::make_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); code = status_codes::BadRequest; } @@ -729,7 +729,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } return true; @@ -773,7 +773,7 @@ namespace nmos // invalid arguments utility::stringstream_t ss; ss << U("parameter error: ") << e.what(); - method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + method_result = nc::details::make_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); code = status_codes::BadRequest; } @@ -782,7 +782,7 @@ namespace nmos // JSON validation error utility::stringstream_t ss; ss << U("JSON validation error: ") << e.what(); - method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + method_result = nc::details::make_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); code = status_codes::BadRequest; } @@ -791,7 +791,7 @@ namespace nmos else { // resource not found for the role path - auto method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); + auto method_result = nc::details::make_method_result_error({ nmos::nc_method_status::bad_oid }, U("Not Found; ") + role_path); set_reply(res, status_codes::NotFound, method_result); } diff --git a/Development/nmos/configuration_methods.cpp b/Development/nmos/configuration_methods.cpp index 1a3a3090a..5d795d369 100644 --- a/Development/nmos/configuration_methods.cpp +++ b/Development/nmos/configuration_methods.cpp @@ -19,7 +19,7 @@ namespace nmos value property_holders = value::array(); - nmos::nc_class_id class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + nmos::nc_class_id class_id = nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(resource.data)); // make NcPropertyHolder objects while (!class_id.empty()) @@ -29,7 +29,7 @@ namespace nmos for (const auto& property_descriptor : control_class_descriptor.property_descriptors.as_array()) { const auto descriptor = include_descriptors ? property_descriptor : value::null(); - value property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)), descriptor, resource.data.at(nmos::fields::nc::name(property_descriptor))); + value property_holder = nmos::nc::details::make_property_holder(nmos::nc::details::parse_property_id(nmos::fields::nc::id(property_descriptor)), descriptor, resource.data.at(nmos::fields::nc::name(property_descriptor))); web::json::push_back(property_holders, property_holder); } @@ -55,12 +55,12 @@ namespace nmos const auto& dependency_paths = nmos::fields::nc::dependency_paths(resource.data); const auto& allowed_member_classes = nmos::fields::nc::allowed_members_classes(resource.data); - auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path, property_holders, dependency_paths, allowed_member_classes, nmos::fields::nc::is_rebuildable(resource.data)); + auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path, property_holders, dependency_paths, allowed_member_classes, nmos::fields::nc::is_rebuildable(resource.data)); web::json::push_back(object_properties_holders, object_properties_holder); // Recurse into members...if we want to...and the object has them - if (recurse && nmos::nc::is_block(nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)))) + if (recurse && nmos::nc::is_block(nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(resource.data)))) { if (resource.data.has_field(nmos::fields::nc::members)) { @@ -95,9 +95,9 @@ namespace nmos validation_fingerprint = create_validation_fingerprint(resources, resource); } - auto bulk_properties_holder = nmos::nc::details::make_nc_bulk_properties_holder(validation_fingerprint, object_properties_holders); + auto bulk_properties_holder = nmos::nc::details::make_bulk_properties_holder(validation_fingerprint, object_properties_holders); - return nmos::nc::details::make_nc_method_result({ nmos::nc_method_status::ok }, bulk_properties_holder); + return nmos::nc::details::make_method_result({ nmos::nc_method_status::ok }, bulk_properties_holder); } web::json::value validate_set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, nmos::nc_restore_mode::restore_mode restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) @@ -108,14 +108,14 @@ namespace nmos if (!validate_validation_fingerprint(resources, resource, validation_fingerprint.c_str())) { - return nmos::nc::details::make_nc_method_result_error({ nmos::nc_method_status::invalid_request }, U("Invalid validation fingerprint")); + return nmos::nc::details::make_method_result_error({ nmos::nc_method_status::invalid_request }, U("Invalid validation fingerprint")); } } const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); const auto object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, true, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); - return nmos::nc::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); + return nmos::nc::details::make_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } web::json::value set_properties_by_path(nmos::resources& resources, const nmos::resource& resource, const web::json::value& backup_data_set, bool recurse, nmos::nc_restore_mode::restore_mode restore_mode, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, nmos::remove_device_model_object_handler remove_device_model_object, nmos::create_device_model_object_handler create_device_model_object) @@ -126,13 +126,13 @@ namespace nmos if (!validate_validation_fingerprint(resources, resource, validation_fingerprint.c_str())) { - return nmos::nc::details::make_nc_method_result_error({ nmos::nc_method_status::invalid_request }, U("Invalid validation fingerprint")); + return nmos::nc::details::make_method_result_error({ nmos::nc_method_status::invalid_request }, U("Invalid validation fingerprint")); } } const auto& object_properties_holders = nmos::fields::nc::values(backup_data_set); const auto object_properties_set_validation = apply_backup_data_set(resources, resource, object_properties_holders, recurse, restore_mode, false, get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); - return nmos::nc::details::make_nc_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); + return nmos::nc::details::make_method_result({ nmos::nc_method_status::ok }, object_properties_set_validation); } } \ No newline at end of file diff --git a/Development/nmos/configuration_resources.cpp b/Development/nmos/configuration_resources.cpp index 0df84975d..4e24a7f55 100644 --- a/Development/nmos/configuration_resources.cpp +++ b/Development/nmos/configuration_resources.cpp @@ -8,21 +8,21 @@ namespace nmos { web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::array& notices, const utility::string_t& status_message) { - return nc::details::make_nc_object_properties_set_validation(role_path, status, notices, web::json::value::string(status_message)); + return nc::details::make_object_properties_set_validation(role_path, status, notices, web::json::value::string(status_message)); } web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const web::json::array& notices) { - return nc::details::make_nc_object_properties_set_validation(role_path, status, notices, web::json::value::null()); + return nc::details::make_object_properties_set_validation(role_path, status, notices, web::json::value::null()); } web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status) { - return nc::details::make_nc_object_properties_set_validation(role_path, status, web::json::value::array().as_array(), web::json::value::null()); + return nc::details::make_object_properties_set_validation(role_path, status, web::json::value::array().as_array(), web::json::value::null()); } web::json::value make_object_properties_set_validation(const web::json::array& role_path, const nmos::nc_restore_validation_status::status status, const utility::string_t& status_message) { - return nc::details::make_nc_object_properties_set_validation(role_path, status, web::json::value::array().as_array(), web::json::value::string(status_message)); + return nc::details::make_object_properties_set_validation(role_path, status, web::json::value::array().as_array(), web::json::value::string(status_message)); } } diff --git a/Development/nmos/configuration_utils.cpp b/Development/nmos/configuration_utils.cpp index 9beb71702..849befb3a 100644 --- a/Development/nmos/configuration_utils.cpp +++ b/Development/nmos/configuration_utils.cpp @@ -17,13 +17,13 @@ namespace nmos { bool is_property_value_valid(web::json::value& property_restore_notices, const web::json::value& property_value, const web::json::value& property_descriptor, nmos::nc_restore_mode::restore_mode restore_mode, bool is_rebuildable) { - const nmos::nc_property_id& property_id = nc::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor)); + const nmos::nc_property_id& property_id = nc::details::parse_property_id(nmos::fields::nc::id(property_descriptor)); bool is_valid = true; // Only allow modification of read only properties when in Rebuild mode if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && restore_mode != nmos::nc_restore_mode::restore_mode::rebuild) { - const auto& property_restore_notice = nc::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::error, U("read only properties can not be modified in Modify restore mode.")); + const auto& property_restore_notice = nc::details::make_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::error, U("read only properties can not be modified in Modify restore mode.")); web::json::push_back(property_restore_notices, property_restore_notice); is_valid = false; } @@ -31,7 +31,7 @@ namespace nmos if (bool(nmos::fields::nc::is_read_only(property_descriptor)) && !is_rebuildable) { - const auto& property_restore_notice = nc::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::error, U("object must be rebuildable to allow modification of read only properties.")); + const auto& property_restore_notice = nc::details::make_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::error, U("object must be rebuildable to allow modification of read only properties.")); web::json::push_back(property_restore_notices, property_restore_notice); is_valid = false; } @@ -43,7 +43,7 @@ namespace nmos { for (const auto& property_value : property_values) { - const auto& property_id = nc::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_id = nc::details::parse_property_id(nmos::fields::nc::id(property_value)); const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); if (bool(nmos::fields::nc::is_read_only(property_descriptor))) @@ -56,7 +56,7 @@ namespace nmos web::json::value modify_device_model_object(nmos::resources& resources, const nmos::resource& resource, const web::json::array& target_role_path, const web::json::value& target_object_properties_holder, nmos::nc_restore_mode::restore_mode restore_mode, bool validate, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list) { - const auto& class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + const auto& class_id = nc::details::parse_class_id(nmos::fields::nc::class_id(resource.data)); auto object_properties_set_validation_values = web::json::value::array(); @@ -67,7 +67,7 @@ namespace nmos const auto& filtered_property_values = boost::copy_range>(nmos::fields::nc::values(target_object_properties_holder) | boost::adaptors::filtered([&property_restore_notices, &resource, class_id, get_control_protocol_class_descriptor, restore_mode](const web::json::value& property_value) { - const auto& property_id = nc::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_id = nc::details::parse_property_id(nmos::fields::nc::id(property_value)); const auto& property_descriptor = nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); return resource.data.at(nmos::fields::nc::name(property_descriptor)) != nmos::fields::nc::value(property_value) @@ -82,7 +82,7 @@ namespace nmos const auto& read_only_property_values = boost::copy_range>(filtered_property_values | boost::adaptors::filtered([class_id, get_control_protocol_class_descriptor](const web::json::value& property_value) { - const auto& property_id = nc::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_id = nc::details::parse_property_id(nmos::fields::nc::id(property_value)); const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); return nmos::fields::nc::is_read_only(property_descriptor); @@ -103,7 +103,7 @@ namespace nmos std::vector read_only_property_ids; for (const auto& property_value: read_only_property_values) { - const auto& property_id = nc::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_id = nc::details::parse_property_id(nmos::fields::nc::id(property_value)); // Don't include structural properties - changing these could break the device model if (property_id != nmos::nc_object_class_id_property_id && property_id != nmos::nc_object_oid_property_id && @@ -111,7 +111,7 @@ namespace nmos property_id != nmos::nc_object_owner_property_id && property_id != nmos::nc_object_role_property_id) { - read_only_property_ids.push_back(nc::details::parse_nc_property_id(nmos::fields::nc::id(property_value))); + read_only_property_ids.push_back(nc::details::parse_property_id(nmos::fields::nc::id(property_value))); } } @@ -120,7 +120,7 @@ namespace nmos const auto& allowed_property_values = boost::copy_range>(filtered_property_values | boost::adaptors::filtered([&property_restore_notices, get_control_protocol_class_descriptor, class_id, allow_list_read_only_property_ids](const web::json::value& property_value) { - const auto& property_id = nc::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_id = nc::details::parse_property_id(nmos::fields::nc::id(property_value)); const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); // if it's read only and in the allow list then add it if (!nmos::fields::nc::is_read_only(property_descriptor)) @@ -130,13 +130,13 @@ namespace nmos for (const auto& allowed_property_id: allow_list_read_only_property_ids) { - if (nmos::fields::nc::id(property_value) == nc::details::make_nc_property_id(allowed_property_id)) + if (nmos::fields::nc::id(property_value) == nc::details::make_property_id(allowed_property_id)) { return true; } } // Create a warning notice for any read only property not allowed by the allow list - const auto& property_restore_notice = nc::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::warning, U("This read only property can not be modified.")); + const auto& property_restore_notice = nc::details::make_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::warning, U("This read only property can not be modified.")); web::json::push_back(property_restore_notices, property_restore_notice); return false; @@ -154,7 +154,7 @@ namespace nmos } for (const auto& property_value : property_modify_list) { - const auto& property_id = nc::details::parse_nc_property_id(nmos::fields::nc::id(property_value)); + const auto& property_id = nc::details::parse_property_id(nmos::fields::nc::id(property_value)); const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); // hmmm, ideally we would pass the value into modify_resource with the validate @@ -179,7 +179,7 @@ namespace nmos // Generate notice for this property utility::stringstream_t ss; ss << U("property error: ") << e.what(); - const auto& property_restore_notice = nc::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::error, ss.str()); + const auto& property_restore_notice = nc::details::make_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::error, ss.str()); web::json::push_back(property_restore_notices, property_restore_notice); } } @@ -244,7 +244,7 @@ namespace nmos if (!remove_device_model_object(*found, child_role_path_array, validate)) { // error in user code - web::json::push_back(block_notices, nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Application error."))); + web::json::push_back(block_notices, nc::details::make_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Application error."))); continue; } @@ -258,14 +258,14 @@ namespace nmos else { // unable to delete resource so report the error and don't update block - web::json::push_back(block_notices, nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Unable to delete resource in Device Model."))); + web::json::push_back(block_notices, nc::details::make_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Unable to delete resource in Device Model."))); } } } else { // unable to delete resource so report the error and don't update block - web::json::push_back(block_notices, nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Unable to find resource in Device Model."))); + web::json::push_back(block_notices, nc::details::make_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, U("Unable to find resource in Device Model."))); } } } @@ -312,7 +312,7 @@ namespace nmos if (oid_property_holder != web::json::value::null() && oid != nmos::fields::nc::value(oid_property_holder).as_integer()) { - const auto notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("OID value in block member inconsistent with value in property holder. Property holder value takes precidence.")); + const auto notice = nc::details::make_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("OID value in block member inconsistent with value in property holder. Property holder value takes precidence.")); web::json::push_back(block_notices, notice); } @@ -330,7 +330,7 @@ namespace nmos auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::device_error, status_message); web::json::push_back(object_properties_set_validations, object_properties_set_validation); // also create error notice for the block - const auto block_notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, status_message); + const auto block_notice = nc::details::make_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, status_message); web::json::push_back(block_notices, block_notice); // erase object from object_properties_holder_map so it isn't processed subsequently object_properties_holder_map.erase(child_role_path); @@ -346,7 +346,7 @@ namespace nmos max_oid = std::max(max_oid, nmos::fields::nc::oid(r.data)); } oid = ++max_oid; - const auto block_notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Dynamically generating new OID for new block member.")); + const auto block_notice = nc::details::make_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, U("Dynamically generating new OID for new block member.")); web::json::push_back(block_notices, block_notice); } @@ -359,14 +359,14 @@ namespace nmos { utility::stringstream_t ss; ss << U("Role value in block member inconsistent with value in property holder. Property holder value takes precidence for role=") << role; - const auto notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); + const auto notice = nc::details::make_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); web::json::push_back(block_notices, notice); } if (owner != block_oid) { utility::stringstream_t ss; ss << U("Owner value in block member inconsistent with oid of Block. Block oid takes precidence for role=") << role; - const auto notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); + const auto notice = nc::details::make_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); web::json::push_back(block_notices, notice); } const auto& owner_property_holder = nmos::get_property_holder(child_object_properties_holder->second, nmos::nc_object_owner_property_id); @@ -374,7 +374,7 @@ namespace nmos { utility::stringstream_t ss; ss << U("Owner value in block property holder inconsistent with oid of Block. Block oid takes precidence for role=") << role; - const auto notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("owner"), nmos::nc_property_restore_notice_type::warning, ss.str()); + const auto notice = nc::details::make_property_restore_notice(nmos::nc_block_members_property_id, U("owner"), nmos::nc_property_restore_notice_type::warning, ss.str()); web::json::push_back(added_object_notices, notice); } const auto& constant_oid_property_holder = nmos::get_property_holder(child_object_properties_holder->second, nmos::nc_object_constant_oid_property_id); @@ -384,7 +384,7 @@ namespace nmos { utility::stringstream_t ss; ss << U("Constant OID value in block property holder inconsistent with oid of Block. Block oid takes precidence for role=") << role; - const auto notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); + const auto notice = nc::details::make_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::warning, ss.str()); web::json::push_back(block_notices, notice); } @@ -394,12 +394,12 @@ namespace nmos { utility::stringstream_t ss; ss << U("Class ID property value holder missing for role=") << role; - const auto notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("class_id"), nmos::nc_property_restore_notice_type::error, ss.str()); + const auto notice = nc::details::make_property_restore_notice(nmos::nc_block_members_property_id, U("class_id"), nmos::nc_property_restore_notice_type::error, ss.str()); web::json::push_back(added_object_notices, notice); auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::failed, added_object_notices.as_array(), ss.str()); web::json::push_back(object_properties_set_validations, object_properties_set_validation); // also create error notice for the block - const auto block_notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, ss.str()); + const auto block_notice = nc::details::make_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, ss.str()); web::json::push_back(block_notices, block_notice); // erase object from object_properties_holder_map so it isn't processed subsequently @@ -425,12 +425,12 @@ namespace nmos { utility::stringstream_t ss; ss << U("Device model error: attempting to add unexpected class for role=") << role; - const auto notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("class_id"), nmos::nc_property_restore_notice_type::error, ss.str()); + const auto notice = nc::details::make_property_restore_notice(nmos::nc_block_members_property_id, U("class_id"), nmos::nc_property_restore_notice_type::error, ss.str()); web::json::push_back(added_object_notices, notice); auto object_properties_set_validation = nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::failed, added_object_notices.as_array(), ss.str()); web::json::push_back(object_properties_set_validations, object_properties_set_validation); // also create error notice for the block - const auto block_notice = nc::details::make_nc_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, ss.str()); + const auto block_notice = nc::details::make_property_restore_notice(nmos::nc_block_members_property_id, U("members"), nmos::nc_property_restore_notice_type::error, ss.str()); web::json::push_back(block_notices, block_notice); // erase object from object_properties_holder_map so it isn't processed subsequently @@ -450,9 +450,9 @@ namespace nmos for (const auto& property_holder: nmos::fields::nc::values(child_object_properties_holder->second)) { - property_values.insert(std::pair(nc::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)), nmos::fields::nc::value(property_holder))); + property_values.insert(std::pair(nc::details::parse_property_id(nmos::fields::nc::id(property_holder)), nmos::fields::nc::value(property_holder))); } - auto parsed_class_id = nc::details::parse_nc_class_id(class_id.as_array()); + auto parsed_class_id = nc::details::parse_class_id(class_id.as_array()); auto device_model_object = create_device_model_object(parsed_class_id, oid, constant_oid, owner, role, user_label, touchpoints, validate, property_values); @@ -460,7 +460,7 @@ namespace nmos { for (const auto& property_holder : nmos::fields::nc::values(child_object_properties_holder->second)) { - const auto property_id = nc::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)); + const auto property_id = nc::details::parse_property_id(nmos::fields::nc::id(property_holder)); const auto& property_descriptor = nmos::nc::find_property_descriptor(property_id, parsed_class_id, get_control_protocol_class_descriptor); if (device_model_object.data.has_field(nmos::fields::nc::name(property_descriptor))) @@ -471,14 +471,14 @@ namespace nmos if (object_value != property_holder_value) { // warn - const auto notice = nc::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::warning, U("Property not updated.")); + const auto notice = nc::details::make_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::warning, U("Property not updated.")); web::json::push_back(added_object_notices, notice); } } else { // error doesn't have this property - const auto notice = nc::details::make_nc_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::warning, U("Property not member of created object.")); + const auto notice = nc::details::make_property_restore_notice(property_id, nmos::fields::nc::name(property_descriptor), nmos::nc_property_restore_notice_type::warning, U("Property not member of created object.")); web::json::push_back(added_object_notices, notice); } } @@ -488,7 +488,7 @@ namespace nmos // Add object to device model nmos::nc::insert_resource(resources, std::move(device_model_object)); - auto block_member_descriptor = nc::details::make_nc_block_member_descriptor(block_member_description, role, oid, constant_oid, nmos::nc_receiver_monitor_class_id, block_member_user_label, owner); + auto block_member_descriptor = nc::details::make_block_member_descriptor(block_member_description, role, oid, constant_oid, nmos::nc_receiver_monitor_class_id, block_member_user_label, owner); members_to_add.push_back(block_member_descriptor); } web::json::push_back(object_properties_set_validations, nmos::make_object_properties_set_validation(child_role_path, nmos::nc_restore_validation_status::ok, added_object_notices.as_array())); @@ -586,7 +586,7 @@ namespace nmos const auto& filtered_property_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) | boost::adaptors::filtered([&property_id](const web::json::value& property_holder) { - return property_id == nc::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)); + return property_id == nc::details::parse_property_id(nmos::fields::nc::id(property_holder)); }) ); // There should only be a single property holder for the members @@ -620,7 +620,7 @@ namespace nmos bool is_block_modified(const nmos::resource& resource, const web::json::value& object_properties_holder) { // Are they blocks? - nmos::nc_class_id class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + nmos::nc_class_id class_id = nc::details::parse_class_id(nmos::fields::nc::class_id(resource.data)); if (!nmos::nc::is_block(class_id)) { return false; @@ -628,7 +628,7 @@ namespace nmos const auto& block_members_properties_holders = boost::copy_range>(nmos::fields::nc::values(object_properties_holder) | boost::adaptors::filtered([](const web::json::value& property_holder) { - return nmos::nc_block_members_property_id == nc::details::parse_nc_property_id(nmos::fields::nc::id(property_holder)); + return nmos::nc_block_members_property_id == nc::details::parse_property_id(nmos::fields::nc::id(property_holder)); }) ); // There should only be a single property holder for the members @@ -744,7 +744,7 @@ namespace nmos const auto& object_properties_holder = object_properties_holder_map.at(role_path); - const auto& class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(r->data)); + const auto& class_id = nc::details::parse_class_id(nmos::fields::nc::class_id(r->data)); if (nmos::nc::is_block(class_id) && nmos::fields::nc::is_rebuildable(r->data) && restore_mode == nmos::nc_restore_mode::rebuild && is_block_modified(*r, object_properties_holder)) { diff --git a/Development/nmos/control_protocol_behaviour.cpp b/Development/nmos/control_protocol_behaviour.cpp index b4f99cd12..e3333a711 100644 --- a/Development/nmos/control_protocol_behaviour.cpp +++ b/Development/nmos/control_protocol_behaviour.cpp @@ -87,7 +87,7 @@ namespace nmos for (const auto& descriptor : descriptors.as_array()) { auto oid = nmos::fields::nc::oid(descriptor); - const auto& class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); + const auto& class_id = nc::details::parse_class_id(nmos::fields::nc::class_id(descriptor)); auto status_reporting_delay = nc::get_property(control_protocol_resources, oid, nc_status_monitor_status_reporting_delay, get_control_protocol_class_descriptor, gate); @@ -124,7 +124,7 @@ namespace nmos for (const auto& descriptor : descriptors.as_array()) { const auto& oid = nmos::fields::nc::oid(descriptor); - const auto& class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); + const auto& class_id = nc::details::parse_class_id(nmos::fields::nc::class_id(descriptor)); const auto status_reporting_delay = nc::get_property(control_protocol_resources, oid, nc_status_monitor_status_reporting_delay, get_control_protocol_class_descriptor, gate); diff --git a/Development/nmos/control_protocol_handlers.cpp b/Development/nmos/control_protocol_handlers.cpp index e33b1706b..8ec66b32c 100644 --- a/Development/nmos/control_protocol_handlers.cpp +++ b/Development/nmos/control_protocol_handlers.cpp @@ -55,7 +55,7 @@ namespace nmos auto& method_descriptors = control_class_descriptor.method_descriptors; auto found = std::find_if(method_descriptors.begin(), method_descriptors.end(), [&method_id](const experimental::method& method) { - return method_id == nc::details::parse_nc_method_id(nmos::fields::nc::id(std::get<0>(method))); + return method_id == nc::details::parse_method_id(nmos::fields::nc::id(std::get<0>(method))); }); if (method_descriptors.end() != found) { @@ -92,7 +92,7 @@ namespace nmos const bool active = nmos::fields::master_enable(endpoint_active); auto found = nc::find_resource(resources, nmos::types::nc_status_monitor, connection_resource.id); - if (resources.end() != found && nmos::nc::is_status_monitor(nc::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) + if (resources.end() != found && nmos::nc::is_status_monitor(nc::details::parse_class_id(nmos::fields::nc::class_id(found->data)))) { const auto& oid = nmos::fields::nc::oid(found->data); diff --git a/Development/nmos/control_protocol_methods.cpp b/Development/nmos/control_protocol_methods.cpp index c5a041a25..337cd43ad 100644 --- a/Development/nmos/control_protocol_methods.cpp +++ b/Development/nmos/control_protocol_methods.cpp @@ -23,17 +23,17 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Get property: " << property_id.serialize(); // find the relevant nc_property_descriptor - const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(details::parse_property_id(property_id), details::parse_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { - return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, resource.data.at(nmos::fields::nc::name(property))); + return details::make_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, resource.data.at(nmos::fields::nc::name(property))); } // unknown property utility::ostringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do Get"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); + return details::make_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } // Set property value @@ -47,8 +47,8 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Set property: " << property_id.serialize() << " value: " << val.serialize(); // find the relevant nc_property_descriptor - const auto property_id_ = details::parse_nc_property_id(property_id); - const auto& property = nc::find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto property_id_ = details::parse_property_id(property_id); + const auto& property = nc::find_property_descriptor(property_id_, details::parse_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (nmos::fields::nc::is_read_only(property)) @@ -56,7 +56,7 @@ namespace nmos utility::ostringstream_t ss; ss << U("can not set read only property: ") << property_id.serialize(); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::read_only}, ss.str()); + return details::make_method_result_error({nc_method_status::read_only}, ss.str()); } if ((val.is_null() && !nmos::fields::nc::is_nullable(property)) @@ -66,18 +66,18 @@ namespace nmos utility::ostringstream_t ss; ss << U("parameter error: cannot set value: ") << val.serialize() << U(" on property: ") << property_id.serialize(); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + return details::make_method_result_error({nc_method_status::parameter_error}, ss.str()); } // Special case for BCP-008-01/02 where it specifies that status monitors cannot be disabled if (nmos::fields::nc::name(property).c_str() == nmos::fields::nc::enabled.key - && nc::is_status_monitor(details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data))) + && nc::is_status_monitor(details::parse_class_id(nmos::fields::nc::class_id(resource.data))) && !val.as_bool()) { utility::ostringstream_t ss; ss << U("invalid request: cannot disable NcStatusMonitors"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + return details::make_method_result_error({nc_method_status::invalid_request}, ss.str()); } try @@ -98,14 +98,14 @@ namespace nmos }, make_property_changed_event(nmos::fields::nc::oid(resource.data), {{property_id_, nc_property_change_type::type::value_changed, val}})); - return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}); + return details::make_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}); } catch (const nmos::control_protocol_exception& e) { utility::ostringstream_t ss; ss << "Set property: " << property_id.serialize() << " value: " << val.serialize() << " error: " << e.what(); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + return details::make_method_result_error({nc_method_status::parameter_error}, ss.str()); } } @@ -113,7 +113,7 @@ namespace nmos utility::ostringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << " to do Set"; slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); + return details::make_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } // Get sequence item @@ -127,7 +127,7 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Get sequence item: " << property_id.serialize() << " index: " << index; // find the relevant nc_property_descriptor - const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(details::parse_property_id(property_id), details::parse_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { const auto& data = resource.data.at(nmos::fields::nc::name(property)); @@ -137,26 +137,26 @@ namespace nmos // property is not a sequence utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceItem"); - return details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + return details::make_method_result_error({nc_method_status::invalid_request}, ss.str()); } if (data.as_array().size() > (size_t)index) { - return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, data.at(index)); + return details::make_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, data.at(index)); } // out of bound utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do GetSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::index_out_of_bounds}, ss.str()); + return details::make_method_result_error({nc_method_status::index_out_of_bounds}, ss.str()); } // unknown property utility::ostringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do GetSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); + return details::make_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } // Set sequence item @@ -171,13 +171,13 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize(); // find the relevant nc_property_descriptor - const auto property_id_ = details::parse_nc_property_id(property_id); - const auto& property = nc::find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto property_id_ = details::parse_property_id(property_id); + const auto& property = nc::find_property_descriptor(property_id_, details::parse_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (nmos::fields::nc::is_read_only(property)) { - return details::make_nc_method_result({nc_method_status::read_only}); + return details::make_method_result({nc_method_status::read_only}); } auto& data = resource.data.at(nmos::fields::nc::name(property)); @@ -188,7 +188,7 @@ namespace nmos utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do SetSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + return details::make_method_result_error({nc_method_status::invalid_request}, ss.str()); } if (data.as_array().size() > (size_t)index) @@ -211,14 +211,14 @@ namespace nmos }, make_property_changed_event(nmos::fields::nc::oid(resource.data), {{property_id_, nc_property_change_type::type::sequence_item_changed, val, nc_id(index)}})); - return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}); + return details::make_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}); } catch (const nmos::control_protocol_exception& e) { utility::ostringstream_t ss; ss << "Set sequence item: " << property_id.serialize() << " index: " << index << " value: " << val.serialize() << " error: " << e.what(); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + return details::make_method_result_error({nc_method_status::parameter_error}, ss.str()); } } @@ -226,14 +226,14 @@ namespace nmos utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do SetSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::index_out_of_bounds}, ss.str()); + return details::make_method_result_error({nc_method_status::index_out_of_bounds}, ss.str()); } // unknown property utility::ostringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do SetSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); + return details::make_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } // Add item to sequence @@ -249,13 +249,13 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize(); // find the relevant nc_property_descriptor - const auto property_id_ = details::parse_nc_property_id(property_id); - const auto& property = nc::find_property_descriptor(property_id_, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto property_id_ = details::parse_property_id(property_id); + const auto& property = nc::find_property_descriptor(property_id_, details::parse_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (nmos::fields::nc::is_read_only(property)) { - return details::make_nc_method_result({nc_method_status::read_only}); + return details::make_method_result({nc_method_status::read_only}); } if (!nmos::fields::nc::is_sequence(property)) @@ -263,7 +263,7 @@ namespace nmos // property is not a sequence utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do AddSequenceItem"); - return details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + return details::make_method_result_error({nc_method_status::invalid_request}, ss.str()); } auto& data = resource.data.at(nmos::fields::nc::name(property)); @@ -290,14 +290,14 @@ namespace nmos }, make_property_changed_event(nmos::fields::nc::oid(resource.data), {{property_id_, nc_property_change_type::type::sequence_item_added, val, sequence_item_index}})); - return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, sequence_item_index); + return details::make_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, sequence_item_index); } catch (const nmos::control_protocol_exception& e) { utility::ostringstream_t ss; ss << "Add sequence item: " << property_id.serialize() << " value: " << val.serialize() << " error: " << e.what(); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + return details::make_method_result_error({nc_method_status::parameter_error}, ss.str()); } } @@ -305,7 +305,7 @@ namespace nmos utility::ostringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do AddSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); + return details::make_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } // Delete sequence item @@ -319,12 +319,12 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Remove sequence item: " << property_id.serialize() << " index: " << index; // find the relevant nc_property_descriptor - const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(details::parse_property_id(property_id), details::parse_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (nmos::fields::nc::is_read_only(property)) { - return details::make_nc_method_result({nc_method_status::read_only}); + return details::make_method_result({nc_method_status::read_only}); } const auto& data = resource.data.at(nmos::fields::nc::name(property)); @@ -335,7 +335,7 @@ namespace nmos utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do RemoveSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + return details::make_method_result_error({nc_method_status::invalid_request}, ss.str()); } if (data.as_array().size() > (size_t)index) @@ -351,23 +351,23 @@ namespace nmos property_changed(resource, nmos::fields::nc::name(property), -2); } - }, make_property_changed_event(nmos::fields::nc::oid(resource.data), {{details::parse_nc_property_id(property_id), nc_property_change_type::type::sequence_item_removed, nc_id(index)}})); + }, make_property_changed_event(nmos::fields::nc::oid(resource.data), {{details::parse_property_id(property_id), nc_property_change_type::type::sequence_item_removed, nc_id(index)}})); - return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}); + return details::make_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}); } // out of bound utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is outside the available range to do RemoveSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::index_out_of_bounds}, ss.str()); + return details::make_method_result_error({nc_method_status::index_out_of_bounds}, ss.str()); } // unknown property utility::ostringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << U(" to do RemoveSequenceItem"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); + return details::make_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } // Get sequence length @@ -382,7 +382,7 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Get sequence length: " << property_id.serialize(); // find the relevant nc_property_descriptor - const auto& property = nc::find_property_descriptor(details::parse_nc_property_id(property_id), details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(details::parse_property_id(property_id), details::parse_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { if (!nmos::fields::nc::is_sequence(property)) @@ -391,7 +391,7 @@ namespace nmos utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << U(" is not a sequence to do GetSequenceLength"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + return details::make_method_result_error({nc_method_status::invalid_request}, ss.str()); } const auto& data = resource.data.at(nmos::fields::nc::name(property)); @@ -402,7 +402,7 @@ namespace nmos if (data.is_null()) { // null - return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, value::null()); + return details::make_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, value::null()); } } else @@ -414,17 +414,17 @@ namespace nmos utility::ostringstream_t ss; ss << U("property: ") << property_id.serialize() << " is a null sequence to do GetSequenceLength"; slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::invalid_request}, ss.str()); + return details::make_method_result_error({nc_method_status::invalid_request}, ss.str()); } } - return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, value(uint32_t(data.as_array().size()))); + return details::make_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nmos::fields::nc::is_deprecated(property) ? nc_method_status::property_deprecated : nc_method_status::ok}, value(uint32_t(data.as_array().size()))); } // unknown property utility::ostringstream_t ss; ss << U("unknown property: ") << property_id.serialize() << " to do GetSequenceLength"; slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::property_not_implemented}, ss.str()); + return details::make_method_result_error({nc_method_status::property_not_implemented}, ss.str()); } // NcBlock methods implementation @@ -442,7 +442,7 @@ namespace nmos auto descriptors = value::array(); nmos::nc::get_member_descriptors(resources, resource, recurse, descriptors.as_array()); - return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); + return details::make_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); } // Finds member(s) by path @@ -460,7 +460,7 @@ namespace nmos if (0 == path.size()) { // empty path - return details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty path to do FindMembersByPath")); + return details::make_method_result_error({nc_method_status::parameter_error}, U("empty path to do FindMembersByPath")); } auto descriptors = value::array(); @@ -492,7 +492,7 @@ namespace nmos utility::ostringstream_t ss; ss << U("role: ") << role.as_string() << U(" not found to do FindMembersByPath"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + return details::make_method_result_error({nc_method_status::parameter_error}, ss.str()); } } else @@ -501,12 +501,12 @@ namespace nmos utility::ostringstream_t ss; ss << U("role: ") << role.as_string() << U(" has no members to do FindMembersByPath"); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + return details::make_method_result_error({nc_method_status::parameter_error}, ss.str()); } } web::json::push_back(descriptors, descriptor); - return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); + return details::make_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); } // Finds members with given role name or fragment @@ -526,13 +526,13 @@ namespace nmos if (role.empty()) { // empty role - return details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty role to do FindMembersByRole")); + return details::make_method_result_error({nc_method_status::parameter_error}, U("empty role to do FindMembersByRole")); } auto descriptors = value::array(); nmos::nc::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, descriptors.as_array()); - return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); + return details::make_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); } // Finds members with given class id @@ -542,16 +542,16 @@ namespace nmos using web::json::value; - const auto class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for + const auto class_id = details::parse_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for const auto& include_derived = nmos::fields::nc::include_derived(arguments); // If TRUE it will also include derived class descriptors const auto& recurse = nmos::fields::nc::recurse(arguments); // TRUE to search nested blocks - slog::log(gate, SLOG_FLF) << "Find members with given class id: " << "class_id: " << details::make_nc_class_id(class_id).serialize(); + slog::log(gate, SLOG_FLF) << "Find members with given class id: " << "class_id: " << details::make_class_id(class_id).serialize(); if (class_id.empty()) { // empty class_id - return details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty classId to do FindMembersByClassId")); + return details::make_method_result_error({nc_method_status::parameter_error}, U("empty classId to do FindMembersByClassId")); } // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -559,7 +559,7 @@ namespace nmos auto descriptors = value::array(); nmos::nc::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors.as_array()); - return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); + return details::make_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptors); } // NcClassManager methods implementation @@ -568,15 +568,15 @@ namespace nmos { using web::json::value; - const auto class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for + const auto class_id = details::parse_class_id(nmos::fields::nc::class_id(arguments)); // Class id to search for const auto& include_inherited = nmos::fields::nc::include_inherited(arguments); // If set the descriptor would contain all inherited elements - slog::log(gate, SLOG_FLF) << "Get a single class descriptor: " << "class_id: " << details::make_nc_class_id(class_id).serialize(); + slog::log(gate, SLOG_FLF) << "Get a single class descriptor: " << "class_id: " << details::make_class_id(class_id).serialize(); if (class_id.empty()) { // empty class_id - return details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty classId to do GetControlClass")); + return details::make_method_result_error({nc_method_status::parameter_error}, U("empty classId to do GetControlClass")); } // note, model mutex is already locked by the outer function, so access to control_protocol_resources is OK... @@ -609,13 +609,13 @@ namespace nmos } } const auto descriptor = fixed_role.is_null() - ? details::make_nc_class_descriptor(description, class_id, name, property_descriptors, method_descriptors, event_descriptors) - : details::make_nc_class_descriptor(description, class_id, name, fixed_role.as_string(), property_descriptors, method_descriptors, event_descriptors); + ? details::make_class_descriptor(description, class_id, name, property_descriptors, method_descriptors, event_descriptors) + : details::make_class_descriptor(description, class_id, name, fixed_role.as_string(), property_descriptors, method_descriptors, event_descriptors); - return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptor); + return details::make_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptor); } - return details::make_nc_method_result_error({nc_method_status::parameter_error}, U("classId not found")); + return details::make_method_result_error({nc_method_status::parameter_error}, U("classId not found")); } // Get a single datatype descriptor @@ -631,7 +631,7 @@ namespace nmos if (name.empty()) { // empty name - return details::make_nc_method_result_error({nc_method_status::parameter_error}, U("empty name to do GetDatatype")); + return details::make_method_result_error({nc_method_status::parameter_error}, U("empty name to do GetDatatype")); } const auto& datatype = get_control_protocol_datatype_descriptor(name); @@ -671,10 +671,10 @@ namespace nmos } } - return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptor); + return details::make_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, descriptor); } - return details::make_nc_method_result_error({nc_method_status::parameter_error}, U("name not found")); + return details::make_method_result_error({nc_method_status::parameter_error}, U("name not found")); } // NcReceiverMonitor methods implementation @@ -697,10 +697,10 @@ namespace nmos }); })); - return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, nc_counter_sequence); + return details::make_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}, nc_counter_sequence); } - return details::make_nc_method_result_error({nmos::nc_method_status::method_not_implemented}, U("not implemented")); + return details::make_method_result_error({nmos::nc_method_status::method_not_implemented}, U("not implemented")); } } @@ -749,14 +749,14 @@ namespace nmos std::pair(nc_status_monitor_overall_status_message_property_id, web::json::value::null()), }; - const auto& class_id = details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)); + const auto& class_id = details::parse_class_id(nmos::fields::nc::class_id(resource.data)); // reset all counters const std::vector> property_values = nmos::nc::is_sender_monitor(class_id) ? sender_property_values : receiver_property_values; for (const auto& property_value : property_values) { - const auto& property = nc::find_property_descriptor(property_value.first, details::parse_nc_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(property_value.first, details::parse_class_id(nmos::fields::nc::class_id(resource.data)), get_control_protocol_class_descriptor); if (!property.is_null()) { try @@ -777,9 +777,9 @@ namespace nmos catch (const nmos::control_protocol_exception& e) { utility::ostringstream_t ss; - ss << "Reset counters: " << details::make_nc_property_id(property_value.first).serialize() << " error: " << e.what(); + ss << "Reset counters: " << details::make_property_id(property_value.first).serialize() << " error: " << e.what(); slog::log(gate, SLOG_FLF) << ss.str(); - return details::make_nc_method_result_error({nc_method_status::parameter_error}, ss.str()); + return details::make_method_result_error({nc_method_status::parameter_error}, ss.str()); } } } @@ -789,7 +789,7 @@ namespace nmos reset_monitor(); } - return details::make_nc_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}); + return details::make_method_result({is_deprecated ? nmos::nc_method_status::method_deprecated : nc_method_status::ok}); } } } diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index f00d67a07..015892157 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -10,7 +10,7 @@ namespace nmos { namespace details { - web::json::value make_nc_method_result(const nc_method_result& method_result) + web::json::value make_method_result(const nc_method_result& method_result) { using web::json::value_of; @@ -19,22 +19,22 @@ namespace nmos }); } - web::json::value make_nc_method_result_error(const nc_method_result& method_result, const utility::string_t& error_message) + web::json::value make_method_result_error(const nc_method_result& method_result, const utility::string_t& error_message) { - auto result = make_nc_method_result(method_result); + auto result = make_method_result(method_result); if (!error_message.empty()) { result[nmos::fields::nc::error_message] = web::json::value::string(error_message); } return result; } - web::json::value make_nc_method_result(const nc_method_result& method_result, const web::json::value& value) + web::json::value make_method_result(const nc_method_result& method_result, const web::json::value& value) { - auto result = make_nc_method_result(method_result); + auto result = make_method_result(method_result); result[nmos::fields::nc::value] = value; return result; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncelementid - web::json::value make_nc_element_id(uint16_t level, uint16_t index) + web::json::value make_element_id(uint16_t level, uint16_t index) { using web::json::value_of; @@ -43,47 +43,47 @@ namespace nmos { nmos::fields::nc::index, index } }); } - web::json::value make_nc_element_id(const nc_element_id& id) + web::json::value make_element_id(const nc_element_id& id) { - return make_nc_element_id(id.level, id.index); + return make_element_id(id.level, id.index); } - nc_element_id parse_nc_element_id(const web::json::value& id) + nc_element_id parse_element_id(const web::json::value& id) { return { uint16_t(nmos::fields::nc::level(id)), uint16_t(nmos::fields::nc::index(id)) }; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventid - web::json::value make_nc_event_id(const nc_event_id& id) + web::json::value make_event_id(const nc_event_id& id) { - return make_nc_element_id(id); + return make_element_id(id); } - nc_event_id parse_nc_event_id(const web::json::value& id) + nc_event_id parse_event_id(const web::json::value& id) { - return parse_nc_element_id(id); + return parse_element_id(id); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethodid - web::json::value make_nc_method_id(const nc_method_id& id) + web::json::value make_method_id(const nc_method_id& id) { - return make_nc_element_id(id); + return make_element_id(id); } - nc_method_id parse_nc_method_id(const web::json::value& id) + nc_method_id parse_method_id(const web::json::value& id) { - return parse_nc_element_id(id); + return parse_element_id(id); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyid - web::json::value make_nc_property_id(const nc_property_id& id) + web::json::value make_property_id(const nc_property_id& id) { - return make_nc_element_id(id); + return make_element_id(id); } - nc_property_id parse_nc_property_id(const web::json::value& id) + nc_property_id parse_property_id(const web::json::value& id) { - return parse_nc_element_id(id); + return parse_element_id(id); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassid - web::json::value make_nc_class_id(const nc_class_id& class_id) + web::json::value make_class_id(const nc_class_id& class_id) { using web::json::value; @@ -91,7 +91,7 @@ namespace nmos for (const auto class_id_item : class_id) { web::json::push_back(nc_class_id, class_id_item); } return nc_class_id; } - nc_class_id parse_nc_class_id(const web::json::array& class_id_) + nc_class_id parse_class_id(const web::json::array& class_id_) { nc_class_id class_id; for (auto& element : class_id_) @@ -102,7 +102,7 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanufacturer - web::json::value make_nc_manufacturer(const utility::string_t& name, const web::json::value& organization_id, const web::json::value& website) + web::json::value make_manufacturer(const utility::string_t& name, const web::json::value& organization_id, const web::json::value& website) { using web::json::value_of; @@ -112,30 +112,30 @@ namespace nmos { nmos::fields::nc::website, website } }); } - web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id, const web::uri& website) + web::json::value make_manufacturer(const utility::string_t& name, nc_organization_id organization_id, const web::uri& website) { using web::json::value; - return make_nc_manufacturer(name, organization_id, value::string(website.to_string())); + return make_manufacturer(name, organization_id, value::string(website.to_string())); } - web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id) + web::json::value make_manufacturer(const utility::string_t& name, nc_organization_id organization_id) { using web::json::value; - return make_nc_manufacturer(name, organization_id, value::null()); + return make_manufacturer(name, organization_id, value::null()); } - web::json::value make_nc_manufacturer(const utility::string_t& name) + web::json::value make_manufacturer(const utility::string_t& name) { using web::json::value; - return make_nc_manufacturer(name, value::null(), value::null()); + return make_manufacturer(name, value::null(), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncproduct // brand_name can be null // uuid can be null // description can be null - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + web::json::value make_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, const web::json::value& brand_name, const web::json::value& uuid, const web::json::value& description) { using web::json::value_of; @@ -149,37 +149,37 @@ namespace nmos { nmos::fields::nc::description, description } }); } - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + web::json::value make_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, const utility::string_t& brand_name, const nc_uuid& uuid, const utility::string_t& description) { using web::json::value; - return make_nc_product(name, key, revision_level, value::string(brand_name), value::string(uuid), value::string(description)); + return make_product(name, key, revision_level, value::string(brand_name), value::string(uuid), value::string(description)); } - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + web::json::value make_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, const utility::string_t& brand_name, const nc_uuid& uuid) { using web::json::value; - return make_nc_product(name, key, revision_level, value::string(brand_name), value::string(uuid), value::null()); + return make_product(name, key, revision_level, value::string(brand_name), value::string(uuid), value::null()); } - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + web::json::value make_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, const utility::string_t& brand_name) { using web::json::value; - return make_nc_product(name, key, revision_level, value::string(brand_name), value::null(), value::null()); + return make_product(name, key, revision_level, value::string(brand_name), value::null(), value::null()); } - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level) + web::json::value make_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level) { using web::json::value; - return make_nc_product(name, key, revision_level, value::null(), value::null(), value::null()); + return make_product(name, key, revision_level, value::null(), value::null(), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdeviceoperationalstate // device_specific_details can be null - web::json::value make_nc_device_operational_state(nc_device_generic_state::state generic_state, const web::json::value& device_specific_details) + web::json::value make_device_operational_state(nc_device_generic_state::state generic_state, const web::json::value& device_specific_details) { using web::json::value_of; @@ -191,7 +191,7 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdescriptor // description can be null - web::json::value make_nc_descriptor(const web::json::value& description) + web::json::value make_descriptor(const web::json::value& description) { using web::json::value_of; @@ -201,36 +201,36 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblockmemberdescriptor // description can be null // user_label can be null - web::json::value make_nc_block_member_descriptor(const web::json::value& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const web::json::value& user_label, nc_oid owner) + web::json::value make_block_member_descriptor(const web::json::value& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const web::json::value& user_label, nc_oid owner) { using web::json::value; - auto data = make_nc_descriptor(description); + auto data = make_descriptor(description); data[nmos::fields::nc::role] = value::string(role); data[nmos::fields::nc::oid] = oid; data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); - data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); + data[nmos::fields::nc::class_id] = make_class_id(class_id); data[nmos::fields::nc::user_label] = user_label; data[nmos::fields::nc::owner] = owner; return data; } - web::json::value make_nc_block_member_descriptor(const utility::string_t& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const utility::string_t& user_label, nc_oid owner) + web::json::value make_block_member_descriptor(const utility::string_t& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const utility::string_t& user_label, nc_oid owner) { using web::json::value; - return make_nc_block_member_descriptor(value::string(description), role, oid, constant_oid, class_id, value::string(user_label), owner); + return make_block_member_descriptor(value::string(description), role, oid, constant_oid, class_id, value::string(user_label), owner); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassdescriptor // description can be null // fixedRole can be null - web::json::value make_nc_class_descriptor(const web::json::value& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) + web::json::value make_class_descriptor(const web::json::value& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) { using web::json::value; - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); + auto data = make_descriptor(description); + data[nmos::fields::nc::class_id] = make_class_id(class_id); data[nmos::fields::nc::name] = value::string(name); data[nmos::fields::nc::fixed_role] = fixed_role; data[nmos::fields::nc::properties] = properties; @@ -239,69 +239,69 @@ namespace nmos return data; } - web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) + web::json::value make_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) { using web::json::value; - return make_nc_class_descriptor(value::string(description), class_id, name, value::string(fixed_role), properties, methods, events); + return make_class_descriptor(value::string(description), class_id, name, value::string(fixed_role), properties, methods, events); } - web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) + web::json::value make_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) { using web::json::value; - return make_nc_class_descriptor(value::string(description), class_id, name, value::null(), properties, methods, events); + return make_class_descriptor(value::string(description), class_id, name, value::null(), properties, methods, events); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncenumitemdescriptor // description can be null - web::json::value make_nc_enum_item_descriptor(const web::json::value& description, const nc_name& name, uint16_t val) + web::json::value make_enum_item_descriptor(const web::json::value& description, const nc_name& name, uint16_t val) { using web::json::value; - auto data = make_nc_descriptor(description); + auto data = make_descriptor(description); data[nmos::fields::nc::name] = value::string(name); data[nmos::fields::nc::value] = val; return data; } - web::json::value make_nc_enum_item_descriptor(const utility::string_t& description, const nc_name& name, uint16_t val) + web::json::value make_enum_item_descriptor(const utility::string_t& description, const nc_name& name, uint16_t val) { using web::json::value; - return make_nc_enum_item_descriptor(value::string(description), name, val); + return make_enum_item_descriptor(value::string(description), name, val); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventdescriptor // description can be null - // id = make_nc_event_id(level, index) - web::json::value make_nc_event_descriptor(const web::json::value& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated) + // id = make_event_id(level, index) + web::json::value make_event_descriptor(const web::json::value& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated) { using web::json::value; - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::id] = make_nc_event_id(id); + auto data = make_descriptor(description); + data[nmos::fields::nc::id] = make_event_id(id); data[nmos::fields::nc::name] = value::string(name); data[nmos::fields::nc::event_datatype] = value::string(event_datatype); data[nmos::fields::nc::is_deprecated] = value::boolean(is_deprecated); return data; } - web::json::value make_nc_event_descriptor(const utility::string_t& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated) + web::json::value make_event_descriptor(const utility::string_t& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated) { using web::json::value; - return make_nc_event_descriptor(value::string(description), id, name, event_datatype, is_deprecated); + return make_event_descriptor(value::string(description), id, name, event_datatype, is_deprecated); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncfielddescriptor // description can be null // type_name can be null // constraints can be null - web::json::value make_nc_field_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + web::json::value make_field_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { using web::json::value; - auto data = make_nc_descriptor(description); + auto data = make_descriptor(description); data[nmos::fields::nc::name] = value::string(name); data[nmos::fields::nc::type_name] = type_name; data[nmos::fields::nc::is_nullable] = value::boolean(is_nullable); @@ -310,29 +310,29 @@ namespace nmos return data; } - web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + web::json::value make_field_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { using web::json::value; - return make_nc_field_descriptor(value::string(description), name, value::string(type_name), is_nullable, is_sequence, constraints); + return make_field_descriptor(value::string(description), name, value::string(type_name), is_nullable, is_sequence, constraints); } - web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + web::json::value make_field_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { using web::json::value; - return make_nc_field_descriptor(value::string(description), name, value::null(), is_nullable, is_sequence, constraints); + return make_field_descriptor(value::string(description), name, value::null(), is_nullable, is_sequence, constraints); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethoddescriptor // description can be null - // id = make_nc_method_id(level, index) + // id = make_method_id(level, index) // sequence parameters - web::json::value make_nc_method_descriptor(const web::json::value& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) + web::json::value make_method_descriptor(const web::json::value& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) { using web::json::value; - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::id] = make_nc_method_id(id); + auto data = make_descriptor(description); + data[nmos::fields::nc::id] = make_method_id(id); data[nmos::fields::nc::name] = value::string(name); data[nmos::fields::nc::result_datatype] = value::string(result_datatype); data[nmos::fields::nc::parameters] = parameters; @@ -340,21 +340,21 @@ namespace nmos return data; } - web::json::value make_nc_method_descriptor(const utility::string_t& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) + web::json::value make_method_descriptor(const utility::string_t& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) { using web::json::value; - return make_nc_method_descriptor(value::string(description), id, name, result_datatype, parameters, is_deprecated); + return make_method_descriptor(value::string(description), id, name, result_datatype, parameters, is_deprecated); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterdescriptor // description can be null // type_name can be null - web::json::value make_nc_parameter_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + web::json::value make_parameter_descriptor(const web::json::value& description, const nc_name& name, const web::json::value& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { using web::json::value; - auto data = make_nc_descriptor(description); + auto data = make_descriptor(description); data[nmos::fields::nc::name] = value::string(name); data[nmos::fields::nc::type_name] = type_name; data[nmos::fields::nc::is_nullable] = value::boolean(is_nullable); @@ -363,29 +363,29 @@ namespace nmos return data; } - web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + web::json::value make_parameter_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { using web::json::value; - return make_nc_parameter_descriptor(value::string(description), name, value::null(), is_nullable, is_sequence, constraints); + return make_parameter_descriptor(value::string(description), name, value::null(), is_nullable, is_sequence, constraints); } - web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) + web::json::value make_parameter_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { using web::json::value; - return make_nc_parameter_descriptor(value::string(description), name, value::string(type_name), is_nullable, is_sequence, constraints); + return make_parameter_descriptor(value::string(description), name, value::string(type_name), is_nullable, is_sequence, constraints); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertydescriptor // description can be null // constraints can be null - web::json::value make_nc_property_descriptor(const web::json::value& description, const nc_property_id& id, const nc_name& name, const web::json::value& type_name, + web::json::value make_property_descriptor(const web::json::value& description, const nc_property_id& id, const nc_name& name, const web::json::value& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) { using web::json::value; - auto data = make_nc_descriptor(description); - data[nmos::fields::nc::id] = make_nc_property_id(id); + auto data = make_descriptor(description); + data[nmos::fields::nc::id] = make_property_id(id); data[nmos::fields::nc::name] = value::string(name); data[nmos::fields::nc::type_name] = type_name; data[nmos::fields::nc::is_read_only] = value::boolean(is_read_only); @@ -396,22 +396,22 @@ namespace nmos return data; } - web::json::value make_nc_property_descriptor(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, + web::json::value make_property_descriptor(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) { using web::json::value; - return nmos::nc::details::make_nc_property_descriptor(value::string(description), id, name, value::string(type_name), is_read_only, is_nullable, is_sequence, is_deprecated, constraints); + return nmos::nc::details::make_property_descriptor(value::string(description), id, name, value::string(type_name), is_read_only, is_nullable, is_sequence, is_deprecated, constraints); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptor // description can be null // constraints can be null - web::json::value make_nc_datatype_descriptor(const web::json::value& description, const nc_name& name, nc_datatype_type::type type, const web::json::value& constraints) + web::json::value make_datatype_descriptor(const web::json::value& description, const nc_name& name, nc_datatype_type::type type, const web::json::value& constraints) { using web::json::value; - auto data = make_nc_descriptor(description); + auto data = make_descriptor(description); data[nmos::fields::nc::name] = value::string(name); data[nmos::fields::nc::type] = type; data[nmos::fields::nc::constraints] = constraints; @@ -423,32 +423,32 @@ namespace nmos // description can be null // constraints can be null // items: sequence - web::json::value make_nc_datatype_descriptor_enum(const web::json::value& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints) + web::json::value make_datatype_descriptor_enum(const web::json::value& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints) { - auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Enum, constraints); + auto data = make_datatype_descriptor(description, name, nc_datatype_type::Enum, constraints); data[nmos::fields::nc::items] = items; return data; } - web::json::value make_nc_datatype_descriptor_enum(const utility::string_t& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints) + web::json::value make_datatype_descriptor_enum(const utility::string_t& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints) { using web::json::value; - return make_nc_datatype_descriptor_enum(value::string(description), name, items, constraints); + return make_datatype_descriptor_enum(value::string(description), name, items, constraints); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorprimitive // description can be null // constraints can be null - web::json::value make_nc_datatype_descriptor_primitive(const web::json::value& description, const nc_name& name, const web::json::value& constraints) + web::json::value make_datatype_descriptor_primitive(const web::json::value& description, const nc_name& name, const web::json::value& constraints) { - return make_nc_datatype_descriptor(description, name, nc_datatype_type::Primitive, constraints); + return make_datatype_descriptor(description, name, nc_datatype_type::Primitive, constraints); } - web::json::value make_nc_datatype_descriptor_primitive(const utility::string_t& description, const nc_name& name, const web::json::value& constraints) + web::json::value make_datatype_descriptor_primitive(const utility::string_t& description, const nc_name& name, const web::json::value& constraints) { using web::json::value; - return make_nc_datatype_descriptor_primitive(value::string(description), name, constraints); + return make_datatype_descriptor_primitive(value::string(description), name, constraints); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorstruct @@ -456,121 +456,121 @@ namespace nmos // constraints can be null // fields: sequence // parent_type can be null - web::json::value make_nc_datatype_descriptor_struct(const web::json::value& description, const nc_name& name, const web::json::value& fields, const web::json::value& parent_type, const web::json::value& constraints) + web::json::value make_datatype_descriptor_struct(const web::json::value& description, const nc_name& name, const web::json::value& fields, const web::json::value& parent_type, const web::json::value& constraints) { - auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Struct, constraints); + auto data = make_datatype_descriptor(description, name, nc_datatype_type::Struct, constraints); data[nmos::fields::nc::fields] = fields; data[nmos::fields::nc::parent_type] = parent_type; return data; } - web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const utility::string_t& parent_type, const web::json::value& constraints) + web::json::value make_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const utility::string_t& parent_type, const web::json::value& constraints) { using web::json::value; - return make_nc_datatype_descriptor_struct(value::string(description), name, fields, value::string(parent_type), constraints); + return make_datatype_descriptor_struct(value::string(description), name, fields, value::string(parent_type), constraints); } - web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const web::json::value& constraints) + web::json::value make_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const web::json::value& constraints) { using web::json::value; - return make_nc_datatype_descriptor_struct(value::string(description), name, fields, value::null(), constraints); + return make_datatype_descriptor_struct(value::string(description), name, fields, value::null(), constraints); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptortypedef // description can be null // constraints can be null - web::json::value make_nc_datatype_typedef(const web::json::value& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints) + web::json::value make_datatype_typedef(const web::json::value& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints) { using web::json::value; - auto data = make_nc_datatype_descriptor(description, name, nc_datatype_type::Typedef, constraints); + auto data = make_datatype_descriptor(description, name, nc_datatype_type::Typedef, constraints); data[nmos::fields::nc::parent_type] = value::string(parent_type); data[nmos::fields::nc::is_sequence] = value::boolean(is_sequence); return data; } - web::json::value make_nc_datatype_typedef(const utility::string_t& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints) + web::json::value make_datatype_typedef(const utility::string_t& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints) { using web::json::value; - return make_nc_datatype_typedef(value::string(description), name, is_sequence, parent_type, constraints); + return make_datatype_typedef(value::string(description), name, is_sequence, parent_type, constraints); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraints - web::json::value make_nc_property_constraints(const nc_property_id& property_id, const web::json::value& default_value) + web::json::value make_property_constraints(const nc_property_id& property_id, const web::json::value& default_value) { using web::json::value_of; return value_of({ - { nmos::fields::nc::property_id, make_nc_property_id(property_id) }, + { nmos::fields::nc::property_id, make_property_id(property_id) }, { nmos::fields::nc::default_value, default_value } }); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsnumber - web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, const web::json::value& default_value, const web::json::value& minimum, const web::json::value& maximum, const web::json::value& step) + web::json::value make_property_constraints_number(const nc_property_id& property_id, const web::json::value& default_value, const web::json::value& minimum, const web::json::value& maximum, const web::json::value& step) { using web::json::value; - auto data = make_nc_property_constraints(property_id, default_value); + auto data = make_property_constraints(property_id, default_value); data[nmos::fields::nc::minimum] = minimum; data[nmos::fields::nc::maximum] = maximum; data[nmos::fields::nc::step] = step; return data; } - web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step) + web::json::value make_property_constraints_number(const nc_property_id& property_id, uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step) { using web::json::value; - return make_nc_property_constraints_number(property_id, value(default_value), value(minimum), value(maximum), value(step)); + return make_property_constraints_number(property_id, value(default_value), value(minimum), value(maximum), value(step)); } - web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t minimum, uint64_t maximum, uint64_t step) + web::json::value make_property_constraints_number(const nc_property_id& property_id, uint64_t minimum, uint64_t maximum, uint64_t step) { using web::json::value; - return make_nc_property_constraints_number(property_id, value::null(), minimum, maximum, step); + return make_property_constraints_number(property_id, value::null(), minimum, maximum, step); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsstring - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const web::json::value& default_value, const web::json::value& max_characters, const web::json::value& pattern) + web::json::value make_property_constraints_string(const nc_property_id& property_id, const web::json::value& default_value, const web::json::value& max_characters, const web::json::value& pattern) { using web::json::value; - auto data = make_nc_property_constraints(property_id, default_value); + auto data = make_property_constraints(property_id, default_value); data[nmos::fields::nc::max_characters] = max_characters; data[nmos::fields::nc::pattern] = pattern; return data; } - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern) + web::json::value make_property_constraints_string(const nc_property_id& property_id, const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern) { using web::json::value; - return make_nc_property_constraints_string(property_id, value::string(default_value), max_characters, value::string(pattern)); + return make_property_constraints_string(property_id, value::string(default_value), max_characters, value::string(pattern)); } - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters, const nc_regex& pattern) + web::json::value make_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters, const nc_regex& pattern) { using web::json::value; - return make_nc_property_constraints_string(property_id, value::null(), max_characters, value::string(pattern)); + return make_property_constraints_string(property_id, value::null(), max_characters, value::string(pattern)); } - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters) + web::json::value make_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters) { using web::json::value; - return make_nc_property_constraints_string(property_id, value::null(), max_characters, value::null()); + return make_property_constraints_string(property_id, value::null(), max_characters, value::null()); } - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const nc_regex& pattern) + web::json::value make_property_constraints_string(const nc_property_id& property_id, const nc_regex& pattern) { using web::json::value; - return make_nc_property_constraints_string(property_id, value::null(), value::null(), value::string(pattern)); + return make_property_constraints_string(property_id, value::null(), value::null(), value::string(pattern)); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraints - web::json::value make_nc_parameter_constraints(const web::json::value& default_value) + web::json::value make_parameter_constraints(const web::json::value& default_value) { using web::json::value_of; @@ -580,66 +580,66 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber - web::json::value make_nc_parameter_constraints_number(const web::json::value& default_value, const web::json::value& minimum, const web::json::value& maximum, const web::json::value& step) + web::json::value make_parameter_constraints_number(const web::json::value& default_value, const web::json::value& minimum, const web::json::value& maximum, const web::json::value& step) { using web::json::value; - auto data = make_nc_parameter_constraints(default_value); + auto data = make_parameter_constraints(default_value); data[nmos::fields::nc::minimum] = minimum; data[nmos::fields::nc::maximum] = maximum; data[nmos::fields::nc::step] = step; return data; } - web::json::value make_nc_parameter_constraints_number(uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step) + web::json::value make_parameter_constraints_number(uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step) { using web::json::value; - return make_nc_parameter_constraints_number(value(default_value), value(minimum), value(maximum), value(step)); + return make_parameter_constraints_number(value(default_value), value(minimum), value(maximum), value(step)); } - web::json::value make_nc_parameter_constraints_number(uint64_t minimum, uint64_t maximum, uint64_t step) + web::json::value make_parameter_constraints_number(uint64_t minimum, uint64_t maximum, uint64_t step) { using web::json::value; - return make_nc_parameter_constraints_number(value::null(), minimum, maximum, step); + return make_parameter_constraints_number(value::null(), minimum, maximum, step); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring - web::json::value make_nc_parameter_constraints_string(const web::json::value& default_value, const web::json::value& max_characters, const web::json::value& pattern) + web::json::value make_parameter_constraints_string(const web::json::value& default_value, const web::json::value& max_characters, const web::json::value& pattern) { - auto data = make_nc_parameter_constraints(default_value); + auto data = make_parameter_constraints(default_value); data[nmos::fields::nc::max_characters] = max_characters; data[nmos::fields::nc::pattern] = pattern; return data; } - web::json::value make_nc_parameter_constraints_string(const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern) + web::json::value make_parameter_constraints_string(const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern) { using web::json::value; - return make_nc_parameter_constraints_string(value::string(default_value), max_characters, value::string(pattern)); + return make_parameter_constraints_string(value::string(default_value), max_characters, value::string(pattern)); } - web::json::value make_nc_parameter_constraints_string(uint32_t max_characters, const nc_regex& pattern) + web::json::value make_parameter_constraints_string(uint32_t max_characters, const nc_regex& pattern) { using web::json::value; - return make_nc_parameter_constraints_string(value::null(), max_characters, value::string(pattern)); + return make_parameter_constraints_string(value::null(), max_characters, value::string(pattern)); } - web::json::value make_nc_parameter_constraints_string(uint32_t max_characters) + web::json::value make_parameter_constraints_string(uint32_t max_characters) { using web::json::value; - return make_nc_parameter_constraints_string(value::null(), max_characters, value::null()); + return make_parameter_constraints_string(value::null(), max_characters, value::null()); } - web::json::value make_nc_parameter_constraints_string(const nc_regex& pattern) + web::json::value make_parameter_constraints_string(const nc_regex& pattern) { using web::json::value; - return make_nc_parameter_constraints_string(value::null(), value::null(), value::string(pattern)); + return make_parameter_constraints_string(value::null(), value::null(), value::string(pattern)); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresource - web::json::value make_nc_touchpoint_resource(const nc_touchpoint_resource& resource) + web::json::value make_touchpoint_resource(const nc_touchpoint_resource& resource) { using web::json::value_of; @@ -649,29 +649,29 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresourcenmos - web::json::value make_nc_touchpoint_resource_nmos(const nc_touchpoint_resource_nmos& resource) + web::json::value make_touchpoint_resource_nmos(const nc_touchpoint_resource_nmos& resource) { using web::json::value; - auto data = make_nc_touchpoint_resource(resource); + auto data = make_touchpoint_resource(resource); data[nmos::fields::nc::id] = value::string(resource.id); return data; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointresourcenmoschannelmapping - web::json::value make_nc_touchpoint_resource_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource) + web::json::value make_touchpoint_resource_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource) { using web::json::value; - auto data = make_nc_touchpoint_resource_nmos(resource); + auto data = make_touchpoint_resource_nmos(resource); data[nmos::fields::nc::io_id] = value::string(resource.io_id); return data; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpoint - web::json::value make_nc_touchpoint(const utility::string_t& context_namespace) + web::json::value make_touchpoint(const utility::string_t& context_namespace) { using web::json::value_of; @@ -681,31 +681,31 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointnmos - web::json::value make_nc_touchpoint_nmos(const nc_touchpoint_resource_nmos& resource) + web::json::value make_touchpoint_nmos(const nc_touchpoint_resource_nmos& resource) { - auto data = make_nc_touchpoint(U("x-nmos")); - data[nmos::fields::nc::resource] = make_nc_touchpoint_resource_nmos(resource); + auto data = make_touchpoint(U("x-nmos")); + data[nmos::fields::nc::resource] = make_touchpoint_resource_nmos(resource); return data; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointnmoschannelmapping - web::json::value make_nc_touchpoint_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource) + web::json::value make_touchpoint_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource) { - auto data = make_nc_touchpoint(U("x-nmos/channelmapping")); - data[nmos::fields::nc::resource] = make_nc_touchpoint_resource_nmos_channel_mapping(resource); + auto data = make_touchpoint(U("x-nmos/channelmapping")); + data[nmos::fields::nc::resource] = make_touchpoint_resource_nmos_channel_mapping(resource); return data; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject - web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + web::json::value make_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { using web::json::value; const auto id = utility::conversions::details::to_string_t(oid); auto data = nmos::details::make_resource_core(id, user_label.is_null() ? U("") : user_label.as_string(), description); // required for nmos::resource - data[nmos::fields::nc::class_id] = make_nc_class_id(class_id); + data[nmos::fields::nc::class_id] = make_class_id(class_id); data[nmos::fields::nc::oid] = oid; data[nmos::fields::nc::constant_oid] = value::boolean(constant_oid); data[nmos::fields::nc::owner] = owner; @@ -727,11 +727,11 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock - web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members) + web::json::value make_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members) { using web::json::value; - auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); + auto data = details::make_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); data[nmos::fields::nc::enabled] = value::boolean(enabled); data[nmos::fields::nc::members] = members; @@ -739,22 +739,22 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker - web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled) + web::json::value make_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled) { using web::json::value; - auto data = details::make_nc_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); + auto data = details::make_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); data[nmos::fields::nc::enabled] = value::boolean(enabled); return data; } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstatusmonitor - web::json::value make_nc_status_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, uint64_t status_reporting_delay) + web::json::value make_status_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, uint64_t status_reporting_delay) { using web::json::value; - auto data = make_nc_worker(class_id, oid, constant_oid, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, enabled); + auto data = make_worker(class_id, oid, constant_oid, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, enabled); data[nmos::fields::nc::overall_status] = value::number(overall_status); data[nmos::fields::nc::overall_status_message] = value::string(overall_status_message); data[nmos::fields::nc::status_reporting_delay] = value::number(status_reporting_delay); @@ -767,7 +767,7 @@ namespace nmos { using web::json::value; - auto data = make_nc_status_monitor(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints, enabled, overall_status, overall_status_message, status_reporting_delay); + auto data = make_status_monitor(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints, enabled, overall_status, overall_status_message, status_reporting_delay); data[nmos::fields::nc::link_status] = value::number(link_status); data[nmos::fields::nc::link_status_message] = value::string(link_status_message); @@ -807,7 +807,7 @@ namespace nmos { using web::json::value; - auto data = make_nc_status_monitor(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints, enabled, overall_status, overall_status_message, status_reporting_delay); + auto data = make_status_monitor(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints, enabled, overall_status, overall_status_message, status_reporting_delay); data[nmos::fields::nc::link_status] = value::number(link_status); data[nmos::fields::nc::link_status_message] = value::string(link_status_message); @@ -843,19 +843,19 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager - web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + web::json::value make_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { - return make_nc_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); + return make_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager - web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, + web::json::value make_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause) { using web::json::value; - auto data = details::make_nc_manager(nc_device_manager_class_id, oid, true, owner, U("DeviceManager"), user_label, description, touchpoints, runtime_property_constraints); + auto data = details::make_manager(nc_device_manager_class_id, oid, true, owner, U("DeviceManager"), user_label, description, touchpoints, runtime_property_constraints); data[nmos::fields::nc::nc_version] = value::string(U("v1.0.0")); data[nmos::fields::nc::manufacturer] = manufacturer; data[nmos::fields::nc::product] = product; @@ -871,11 +871,11 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state) + web::json::value make_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state) { using web::json::value; - auto data = make_nc_manager(nc_class_manager_class_id, oid, true, owner, U("ClassManager"), user_label, description, touchpoints, runtime_property_constraints); + auto data = make_manager(nc_class_manager_class_id, oid, true, owner, U("ClassManager"), user_label, description, touchpoints, runtime_property_constraints); auto lock = control_protocol_state.read_lock(); @@ -890,8 +890,8 @@ namespace nmos for (const auto& method_descriptor : ctl_class.method_descriptors) { web::json::push_back(method_descriptors, std::get<0>(method_descriptor)); } const auto class_description = ctl_class.fixed_role.is_null() - ? make_nc_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.property_descriptors, method_descriptors, ctl_class.event_descriptors) - : make_nc_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.fixed_role.as_string(), ctl_class.property_descriptors, method_descriptors, ctl_class.event_descriptors); + ? make_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.property_descriptors, method_descriptors, ctl_class.event_descriptors) + : make_class_descriptor(ctl_class.description, ctl_class.class_id, ctl_class.name, ctl_class.fixed_role.as_string(), ctl_class.property_descriptors, method_descriptors, ctl_class.event_descriptors); web::json::push_back(control_classes, class_description); } @@ -907,12 +907,12 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertychangedeventdata - web::json::value make_nc_property_changed_event_data(const nc_property_changed_event_data& property_changed_event_data) + web::json::value make_property_changed_event_data(const nc_property_changed_event_data& property_changed_event_data) { using web::json::value_of; return value_of({ - { nmos::fields::nc::property_id, details::make_nc_property_id(property_changed_event_data.property_id) }, + { nmos::fields::nc::property_id, details::make_property_id(property_changed_event_data.property_id) }, { nmos::fields::nc::change_type, property_changed_event_data.change_type }, { nmos::fields::nc::value, property_changed_event_data.value }, { nmos::fields::nc::sequence_item_index, property_changed_event_data.sequence_item_index } @@ -921,17 +921,17 @@ namespace nmos } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager - web::json::value make_nc_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value &user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) + web::json::value make_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value &user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { using web::json::value; - auto data = make_nc_manager(nc_bulk_properties_manager_class_id, oid, true, owner, U("BulkPropertiesManager"), user_label, description, touchpoints, runtime_property_constraints); + auto data = make_manager(nc_bulk_properties_manager_class_id, oid, true, owner, U("BulkPropertiesManager"), user_label, description, touchpoints, runtime_property_constraints); return data; } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesholder - web::json::value make_nc_bulk_properties_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders) + web::json::value make_bulk_properties_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders) { using web::json::value_of; @@ -943,20 +943,20 @@ namespace nmos } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyholder - web::json::value make_nc_property_holder(const nc_property_id& property_id, const web::json::value& descriptor, const web::json::value& property_value) + web::json::value make_property_holder(const nc_property_id& property_id, const web::json::value& descriptor, const web::json::value& property_value) { using web::json::value; using web::json::value_of; return value_of({ - { nmos::fields::nc::id, make_nc_property_id(property_id)}, + { nmos::fields::nc::id, make_property_id(property_id)}, { nmos::fields::nc::descriptor, descriptor}, { nmos::fields::nc::value, property_value}, }, true); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiesholder - web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_holders, const web::json::array& dependency_paths, const web::json::array& allowed_members_classes, bool is_rebuildable) + web::json::value make_object_properties_holder(const web::json::array& role_path, const web::json::array& property_holders, const web::json::array& dependency_paths, const web::json::array& allowed_members_classes, bool is_rebuildable) { using web::json::value_of; @@ -972,13 +972,13 @@ namespace nmos } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenotice - web::json::value make_nc_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message) + web::json::value make_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message) { using web::json::value; using web::json::value_of; return value_of({ - { nmos::fields::nc::id, make_nc_property_id(property_id)}, + { nmos::fields::nc::id, make_property_id(property_id)}, { nmos::fields::nc::name, value::string(name)}, { nmos::fields::nc::notice_type, value::number(notice_type)}, { nmos::fields::nc::notice_message, value::string(notice_message)} @@ -987,7 +987,7 @@ namespace nmos } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiessetvalidation - web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const web::json::value& status_message) + web::json::value make_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const web::json::value& status_message) { using web::json::value; using web::json::value_of; @@ -1044,8 +1044,8 @@ namespace nmos return value_of({ { nmos::fields::nc::oid, oid }, - { nmos::fields::nc::event_id, details::make_nc_event_id(event_id)}, - { nmos::fields::nc::event_data, details::make_nc_property_changed_event_data(property_changed_event_data) } + { nmos::fields::nc::event_id, details::make_event_id(event_id)}, + { nmos::fields::nc::event_data, details::make_property_changed_event_data(property_changed_event_data) } }); } web::json::value make_control_protocol_notification_message(const web::json::value& notifications) @@ -1087,126 +1087,126 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject - web::json::value make_nc_object_properties() + web::json::value make_object_properties() { using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Static value. All instances of the same class will have the same identity value"), nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Object identifier"), nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE iff OID is hardwired into device"), nc_object_constant_oid_property_id, nmos::fields::nc::constant_oid, U("NcBoolean"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("OID of containing block. Can only ever be null for the root block"), nc_object_owner_property_id, nmos::fields::nc::owner, U("NcOid"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Role of object in the containing block"), nc_object_role_property_id, nmos::fields::nc::role, U("NcString"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Scribble strip"), nc_object_user_label_property_id, nmos::fields::nc::user_label, U("NcString"), false, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Touchpoints to other contexts"), nc_object_touchpoints_property_id, nmos::fields::nc::touchpoints, U("NcTouchpoint"), true, true, true, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Runtime property constraints"), nc_object_runtime_property_constraints_property_id, nmos::fields::nc::runtime_property_constraints, U("NcPropertyConstraints"), true, true, true, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Static value. All instances of the same class will have the same identity value"), nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Object identifier"), nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("TRUE iff OID is hardwired into device"), nc_object_constant_oid_property_id, nmos::fields::nc::constant_oid, U("NcBoolean"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("OID of containing block. Can only ever be null for the root block"), nc_object_owner_property_id, nmos::fields::nc::owner, U("NcOid"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Role of object in the containing block"), nc_object_role_property_id, nmos::fields::nc::role, U("NcString"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Scribble strip"), nc_object_user_label_property_id, nmos::fields::nc::user_label, U("NcString"), false, true, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Touchpoints to other contexts"), nc_object_touchpoints_property_id, nmos::fields::nc::touchpoints, U("NcTouchpoint"), true, true, true, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Runtime property constraints"), nc_object_runtime_property_constraints_property_id, nmos::fields::nc::runtime_property_constraints, U("NcPropertyConstraints"), true, true, true, false, value::null())); return properties; } - web::json::value make_nc_object_methods() + web::json::value make_object_methods() { using web::json::value; auto methods = value::array(); { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get property value"), nc_object_get_method_id, U("Get"), U("NcMethodResultPropertyValue"), parameters, false)); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(methods, details::make_method_descriptor(U("Get property value"), nc_object_get_method_id, U("Get"), U("NcMethodResultPropertyValue"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Set property value"), nc_object_set_method_id, U("Set"), U("NcMethodResult"), parameters, false)); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); + web::json::push_back(methods, details::make_method_descriptor(U("Set property value"), nc_object_set_method_id, U("Set"), U("NcMethodResult"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get sequence item"), nc_object_get_sequence_item_method_id, U("GetSequenceItem"), U("NcMethodResultPropertyValue"), parameters, false)); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + web::json::push_back(methods, details::make_method_descriptor(U("Get sequence item"), nc_object_get_sequence_item_method_id, U("GetSequenceItem"), U("NcMethodResultPropertyValue"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Set sequence item value"), nc_object_set_sequence_item_method_id, U("SetSequenceItem"), U("NcMethodResult"), parameters, false)); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); + web::json::push_back(methods, details::make_method_descriptor(U("Set sequence item value"), nc_object_set_sequence_item_method_id, U("SetSequenceItem"), U("NcMethodResult"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id,U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Add item to sequence"), nc_object_add_sequence_item_method_id, U("AddSequenceItem"), U("NcMethodResultId"), parameters, false)); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Property id"), nmos::fields::nc::id,U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); + web::json::push_back(methods, details::make_method_descriptor(U("Add item to sequence"), nc_object_add_sequence_item_method_id, U("AddSequenceItem"), U("NcMethodResultId"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Delete sequence item"), nc_object_remove_sequence_item_method_id, U("RemoveSequenceItem"), U("NcMethodResult"), parameters, false)); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + web::json::push_back(methods, details::make_method_descriptor(U("Delete sequence item"), nc_object_remove_sequence_item_method_id, U("RemoveSequenceItem"), U("NcMethodResult"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get sequence length"), nc_object_get_sequence_length_method_id, U("GetSequenceLength"), U("NcMethodResultLength"), parameters, false)); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(methods, details::make_method_descriptor(U("Get sequence length"), nc_object_get_sequence_length_method_id, U("GetSequenceLength"), U("NcMethodResultLength"), parameters, false)); } return methods; } - web::json::value make_nc_object_events() + web::json::value make_object_events() { using web::json::value; auto events = value::array(); - web::json::push_back(events, details::make_nc_event_descriptor(U("Property changed event"), nc_object_property_changed_event_id, U("PropertyChanged"), U("NcPropertyChangedEventData"), false)); + web::json::push_back(events, details::make_event_descriptor(U("Property changed event"), nc_object_property_changed_event_id, U("PropertyChanged"), U("NcPropertyChangedEventData"), false)); return events; } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock - web::json::value make_nc_block_properties() + web::json::value make_block_properties() { using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE if block is functional"), nc_block_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptors of this block's members"), nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("TRUE if block is functional"), nc_block_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Descriptors of this block's members"), nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, value::null())); return properties; } - web::json::value make_nc_block_methods() + web::json::value make_block_methods() { using web::json::value; auto methods = value::array(); { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If recurse is set to true, nested members can be retrieved"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Gets descriptors of members of the block"), nc_block_get_member_descriptors_method_id, U("GetMemberDescriptors"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + web::json::push_back(parameters, details::make_parameter_descriptor(U("If recurse is set to true, nested members can be retrieved"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_method_descriptor(U("Gets descriptors of members of the block"), nc_block_get_member_descriptors_method_id, U("GetMemberDescriptors"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Relative path to search for (MUST not include the role of the block targeted by oid)"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds member(s) by path"), nc_block_find_members_by_path_method_id, U("FindMembersByPath"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Relative path to search for (MUST not include the role of the block targeted by oid)"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(methods, details::make_method_descriptor(U("Finds member(s) by path"), nc_block_find_members_by_path_method_id, U("FindMembersByPath"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Role text to search for"), nmos::fields::nc::role, U("NcString"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Signals if the comparison should be case sensitive"), nmos::fields::nc::case_sensitive, U("NcBoolean"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("TRUE to only return exact matches"), nmos::fields::nc::match_whole_string, U("NcBoolean"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("TRUE to search nested blocks"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds members with given role name or fragment"), nc_block_find_members_by_role_method_id, U("FindMembersByRole"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Role text to search for"), nmos::fields::nc::role, U("NcString"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Signals if the comparison should be case sensitive"), nmos::fields::nc::case_sensitive, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("TRUE to only return exact matches"), nmos::fields::nc::match_whole_string, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("TRUE to search nested blocks"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_method_descriptor(U("Finds members with given role name or fragment"), nc_block_find_members_by_role_method_id, U("FindMembersByRole"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Class id to search for"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If TRUE it will also include derived class descriptors"), nmos::fields::nc::include_derived, U("NcBoolean"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("TRUE to search nested blocks"), nmos::fields::nc::recurse,U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Finds members with given class id"), nc_block_find_members_by_class_id_method_id, U("FindMembersByClassId"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Class id to search for"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("If TRUE it will also include derived class descriptors"), nmos::fields::nc::include_derived, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("TRUE to search nested blocks"), nmos::fields::nc::recurse,U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_method_descriptor(U("Finds members with given class id"), nc_block_find_members_by_class_id_method_id, U("FindMembersByClassId"), U("NcMethodResultBlockMemberDescriptors"), parameters, false)); } return methods; } - web::json::value make_nc_block_events() + web::json::value make_block_events() { using web::json::value; @@ -1214,22 +1214,22 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker - web::json::value make_nc_worker_properties() + web::json::value make_worker_properties() { using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("TRUE iff worker is enabled"), nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("TRUE iff worker is enabled"), nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, value::null())); return properties; } - web::json::value make_nc_worker_methods() + web::json::value make_worker_methods() { using web::json::value; return value::array(); } - web::json::value make_nc_worker_events() + web::json::value make_worker_events() { using web::json::value; @@ -1237,19 +1237,19 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager - web::json::value make_nc_manager_properties() + web::json::value make_manager_properties() { using web::json::value; return value::array(); } - web::json::value make_nc_manager_methods() + web::json::value make_manager_methods() { using web::json::value; return value::array(); } - web::json::value make_nc_manager_events() + web::json::value make_manager_events() { using web::json::value; @@ -1257,31 +1257,31 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager - web::json::value make_nc_device_manager_properties() + web::json::value make_device_manager_properties() { using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Version of MS-05-02 that this device uses"), nc_device_manager_nc_version_property_id, nmos::fields::nc::nc_version, U("NcVersionCode"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Manufacturer descriptor"), nc_device_manager_manufacturer_property_id, nmos::fields::nc::manufacturer, U("NcManufacturer"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Product descriptor"), nc_device_manager_product_property_id, nmos::fields::nc::product, U("NcProduct"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Serial number"), nc_device_manager_serial_number_property_id, nmos::fields::nc::serial_number, U("NcString"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Asset tracking identifier (user specified)"), nc_device_manager_user_inventory_code_property_id, nmos::fields::nc::user_inventory_code, U("NcString"), false, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Name of this device in the application. Instance name, not product name"), nc_device_manager_device_name_property_id, nmos::fields::nc::device_name, U("NcString"), false, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Role of this device in the application"), nc_device_manager_device_role_property_id, nmos::fields::nc::device_role, U("NcString"), false, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Device operational state"), nc_device_manager_operational_state_property_id, nmos::fields::nc::operational_state, U("NcDeviceOperationalState"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Reason for most recent reset"), nc_device_manager_reset_cause_property_id, nmos::fields::nc::reset_cause, U("NcResetCause"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Arbitrary message from dev to controller"), nc_device_manager_message_property_id, nmos::fields::nc::message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Version of MS-05-02 that this device uses"), nc_device_manager_nc_version_property_id, nmos::fields::nc::nc_version, U("NcVersionCode"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Manufacturer descriptor"), nc_device_manager_manufacturer_property_id, nmos::fields::nc::manufacturer, U("NcManufacturer"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Product descriptor"), nc_device_manager_product_property_id, nmos::fields::nc::product, U("NcProduct"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Serial number"), nc_device_manager_serial_number_property_id, nmos::fields::nc::serial_number, U("NcString"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Asset tracking identifier (user specified)"), nc_device_manager_user_inventory_code_property_id, nmos::fields::nc::user_inventory_code, U("NcString"), false, true, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Name of this device in the application. Instance name, not product name"), nc_device_manager_device_name_property_id, nmos::fields::nc::device_name, U("NcString"), false, true, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Role of this device in the application"), nc_device_manager_device_role_property_id, nmos::fields::nc::device_role, U("NcString"), false, true, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Device operational state"), nc_device_manager_operational_state_property_id, nmos::fields::nc::operational_state, U("NcDeviceOperationalState"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Reason for most recent reset"), nc_device_manager_reset_cause_property_id, nmos::fields::nc::reset_cause, U("NcResetCause"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Arbitrary message from dev to controller"), nc_device_manager_message_property_id, nmos::fields::nc::message, U("NcString"), true, true, false, false, value::null())); return properties; } - web::json::value make_nc_device_manager_methods() + web::json::value make_device_manager_methods() { using web::json::value; return value::array(); } - web::json::value make_nc_device_manager_events() + web::json::value make_device_manager_events() { using web::json::value; @@ -1289,37 +1289,37 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager_properties() + web::json::value make_class_manager_properties() { using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptions of all control classes in the device (descriptors do not contain inherited elements)"), nc_class_manager_control_classes_property_id, nmos::fields::nc::control_classes, U("NcClassDescriptor"), true, false, true, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Descriptions of all data types in the device (descriptors do not contain inherited elements)"), nc_class_manager_datatypes_property_id, nmos::fields::nc::datatypes, U("NcDatatypeDescriptor"), true, false, true, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Descriptions of all control classes in the device (descriptors do not contain inherited elements)"), nc_class_manager_control_classes_property_id, nmos::fields::nc::control_classes, U("NcClassDescriptor"), true, false, true, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Descriptions of all data types in the device (descriptors do not contain inherited elements)"), nc_class_manager_datatypes_property_id, nmos::fields::nc::datatypes, U("NcDatatypeDescriptor"), true, false, true, false, value::null())); return properties; } - web::json::value make_nc_class_manager_methods() + web::json::value make_class_manager_methods() { using web::json::value; auto methods = value::array(); { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("class ID"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::include_inherited, U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get a single class descriptor"), nc_class_manager_get_control_class_method_id, U("GetControlClass"), U("NcMethodResultClassDescriptor"), parameters, false)); + web::json::push_back(parameters, details::make_parameter_descriptor(U("class ID"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::include_inherited, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_method_descriptor(U("Get a single class descriptor"), nc_class_manager_get_control_class_method_id, U("GetControlClass"), U("NcMethodResultClassDescriptor"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("name of datatype"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::include_inherited, U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get a single datatype descriptor"), nc_class_manager_get_datatype_method_id, U("GetDatatype"), U("NcMethodResultDatatypeDescriptor"), parameters, false)); + web::json::push_back(parameters, details::make_parameter_descriptor(U("name of datatype"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::include_inherited, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_method_descriptor(U("Get a single datatype descriptor"), nc_class_manager_get_datatype_method_id, U("GetDatatype"), U("NcMethodResultDatatypeDescriptor"), parameters, false)); } return methods; } - web::json::value make_nc_class_manager_events() + web::json::value make_class_manager_events() { using web::json::value; @@ -1327,24 +1327,24 @@ namespace nmos } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstatusmonitor - web::json::value make_nc_status_monitor_properties() + web::json::value make_status_monitor_properties() { using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Overall status property"), nc_status_monitor_overall_status_property_id, nmos::fields::nc::overall_status, U("NcOverallStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Overall status message property"), nc_status_monitor_overall_status_message_property_id, nmos::fields::nc::overall_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Status reporting delay property (in seconds, default is 3s and 0 means no delay)"), nc_status_monitor_status_reporting_delay, nmos::fields::nc::status_reporting_delay, U("NcUint32"), false, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Overall status property"), nc_status_monitor_overall_status_property_id, nmos::fields::nc::overall_status, U("NcOverallStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Overall status message property"), nc_status_monitor_overall_status_message_property_id, nmos::fields::nc::overall_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Status reporting delay property (in seconds, default is 3s and 0 means no delay)"), nc_status_monitor_status_reporting_delay, nmos::fields::nc::status_reporting_delay, U("NcUint32"), false, false, false, false, value::null())); return properties; } - web::json::value make_nc_status_monitor_methods() + web::json::value make_status_monitor_methods() { using web::json::value; return value::array(); } - web::json::value make_nc_status_monitor_events() + web::json::value make_status_monitor_events() { using web::json::value; @@ -1352,40 +1352,40 @@ namespace nmos } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor - web::json::value make_nc_receiver_monitor_properties() + web::json::value make_receiver_monitor_properties() { using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status property"), nc_receiver_monitor_link_status_property_id, nmos::fields::nc::link_status, U("NcLinkStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status message property"), nc_receiver_monitor_link_status_message_property_id, nmos::fields::nc::link_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status transition counter property"), nc_receiver_monitor_link_status_transition_counter_property_id, nmos::fields::nc::link_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status property"), nc_receiver_monitor_connection_status_property_id, nmos::fields::nc::connection_status, U("NcConnectionStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status message property"), nc_receiver_monitor_connection_status_message_property_id, nmos::fields::nc::connection_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Connection status transition counter property"), nc_receiver_monitor_connection_status_transition_counter_property_id, nmos::fields::nc::connection_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status property"), nc_receiver_monitor_external_synchronization_status_property_id, nmos::fields::nc::external_synchronization_status, U("NcSynchronizationStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status message property"), nc_receiver_monitor_external_synchronization_status_message_property_id, nmos::fields::nc::external_synchronization_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status transition counter property"), nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, nmos::fields::nc::external_synchronization_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Synchronization source id property"), nc_receiver_monitor_synchronization_source_id_property_id, nmos::fields::nc::synchronization_source_id, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Stream status property"), nc_receiver_monitor_stream_status_property_id, nmos::fields::nc::stream_status, U("NcStreamStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Stream status message property"), nc_receiver_monitor_stream_status_message_property_id, nmos::fields::nc::stream_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Stream status property transition counters"), nc_receiver_monitor_stream_status_transition_counter_property_id, nmos::fields::nc::stream_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Automatic reset counters and messages property (default: true)"), nc_receiver_monitor_auto_reset_monitor_property_id, nmos::fields::nc::auto_reset_monitor, U("NcBoolean"), false, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Link status property"), nc_receiver_monitor_link_status_property_id, nmos::fields::nc::link_status, U("NcLinkStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Link status message property"), nc_receiver_monitor_link_status_message_property_id, nmos::fields::nc::link_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Link status transition counter property"), nc_receiver_monitor_link_status_transition_counter_property_id, nmos::fields::nc::link_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Connection status property"), nc_receiver_monitor_connection_status_property_id, nmos::fields::nc::connection_status, U("NcConnectionStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Connection status message property"), nc_receiver_monitor_connection_status_message_property_id, nmos::fields::nc::connection_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Connection status transition counter property"), nc_receiver_monitor_connection_status_transition_counter_property_id, nmos::fields::nc::connection_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("External synchronization status property"), nc_receiver_monitor_external_synchronization_status_property_id, nmos::fields::nc::external_synchronization_status, U("NcSynchronizationStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("External synchronization status message property"), nc_receiver_monitor_external_synchronization_status_message_property_id, nmos::fields::nc::external_synchronization_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("External synchronization status transition counter property"), nc_receiver_monitor_external_synchronization_status_transition_counter_property_id, nmos::fields::nc::external_synchronization_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Synchronization source id property"), nc_receiver_monitor_synchronization_source_id_property_id, nmos::fields::nc::synchronization_source_id, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Stream status property"), nc_receiver_monitor_stream_status_property_id, nmos::fields::nc::stream_status, U("NcStreamStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Stream status message property"), nc_receiver_monitor_stream_status_message_property_id, nmos::fields::nc::stream_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Stream status property transition counters"), nc_receiver_monitor_stream_status_transition_counter_property_id, nmos::fields::nc::stream_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Automatic reset counters and messages property (default: true)"), nc_receiver_monitor_auto_reset_monitor_property_id, nmos::fields::nc::auto_reset_monitor, U("NcBoolean"), false, false, false, false, value::null())); return properties; } - web::json::value make_nc_receiver_monitor_methods() + web::json::value make_receiver_monitor_methods() { using web::json::value; auto methods = value::array(); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Gets the lost packet counters"), nc_receiver_monitor_get_lost_packet_counters_method_id, U("GetLostPacketCounters"), U("NcMethodResultCounters"), value::array(), false)); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Gets the late packet counters"), nc_receiver_monitor_get_late_packet_counters_method_id, U("GetLatePacketCounters"), U("NcMethodResultCounters"), value::array(), false)); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Resets ALL counters"), nc_receiver_monitor_reset_monitor_method_id, U("ResetCountersAndMessages"), U("NcMethodResult"), value::array(), false)); + web::json::push_back(methods, details::make_method_descriptor(U("Gets the lost packet counters"), nc_receiver_monitor_get_lost_packet_counters_method_id, U("GetLostPacketCounters"), U("NcMethodResultCounters"), value::array(), false)); + web::json::push_back(methods, details::make_method_descriptor(U("Gets the late packet counters"), nc_receiver_monitor_get_late_packet_counters_method_id, U("GetLatePacketCounters"), U("NcMethodResultCounters"), value::array(), false)); + web::json::push_back(methods, details::make_method_descriptor(U("Resets ALL counters"), nc_receiver_monitor_reset_monitor_method_id, U("ResetCountersAndMessages"), U("NcMethodResult"), value::array(), false)); return methods; } - web::json::value make_nc_receiver_monitor_events() + web::json::value make_receiver_monitor_events() { using web::json::value; @@ -1393,39 +1393,39 @@ namespace nmos } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsendermonitor - web::json::value make_nc_sender_monitor_properties() + web::json::value make_sender_monitor_properties() { using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status property"), nc_sender_monitor_link_status_property_id, nmos::fields::nc::link_status, U("NcLinkStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status message property"), nc_sender_monitor_link_status_message_property_id, nmos::fields::nc::link_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Link status transition counter property"), nc_sender_monitor_link_status_transition_counter_property_id, nmos::fields::nc::link_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Transmission status property"), nc_sender_monitor_transmission_status_property_id, nmos::fields::nc::transmission_status, U("NcTransmissionStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Transmission status message property"), nc_sender_monitor_transmission_status_message_property_id, nmos::fields::nc::transmission_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Transmission status transition counter property"), nc_sender_monitor_transmission_status_transition_counter_property_id, nmos::fields::nc::transmission_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status property"), nc_sender_monitor_external_synchronization_status_property_id, nmos::fields::nc::external_synchronization_status, U("NcSynchronizationStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status message property"), nc_sender_monitor_external_synchronization_status_message_property_id, nmos::fields::nc::external_synchronization_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("External synchronization status transition counter property"), nc_sender_monitor_external_synchronization_status_transition_counter_property_id, nmos::fields::nc::external_synchronization_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Synchronization source id property"), nc_sender_monitor_synchronization_source_id_property_id, nmos::fields::nc::synchronization_source_id, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Essence status property"), nc_sender_monitor_essence_status_property_id, nmos::fields::nc::essence_status, U("NcEssenceStatus"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Essence status message property"), nc_sender_monitor_essence_status_message_property_id, nmos::fields::nc::essence_status_message, U("NcString"), true, true, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Essence status property transition counters"), nc_sender_monitor_essence_status_transition_counter_property_id, nmos::fields::nc::essence_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Automatic reset counters and messages property (default: true)"), nc_sender_monitor_auto_reset_monitor_property_id, nmos::fields::nc::auto_reset_monitor, U("NcBoolean"), false, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Link status property"), nc_sender_monitor_link_status_property_id, nmos::fields::nc::link_status, U("NcLinkStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Link status message property"), nc_sender_monitor_link_status_message_property_id, nmos::fields::nc::link_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Link status transition counter property"), nc_sender_monitor_link_status_transition_counter_property_id, nmos::fields::nc::link_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Transmission status property"), nc_sender_monitor_transmission_status_property_id, nmos::fields::nc::transmission_status, U("NcTransmissionStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Transmission status message property"), nc_sender_monitor_transmission_status_message_property_id, nmos::fields::nc::transmission_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Transmission status transition counter property"), nc_sender_monitor_transmission_status_transition_counter_property_id, nmos::fields::nc::transmission_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("External synchronization status property"), nc_sender_monitor_external_synchronization_status_property_id, nmos::fields::nc::external_synchronization_status, U("NcSynchronizationStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("External synchronization status message property"), nc_sender_monitor_external_synchronization_status_message_property_id, nmos::fields::nc::external_synchronization_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("External synchronization status transition counter property"), nc_sender_monitor_external_synchronization_status_transition_counter_property_id, nmos::fields::nc::external_synchronization_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Synchronization source id property"), nc_sender_monitor_synchronization_source_id_property_id, nmos::fields::nc::synchronization_source_id, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Essence status property"), nc_sender_monitor_essence_status_property_id, nmos::fields::nc::essence_status, U("NcEssenceStatus"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Essence status message property"), nc_sender_monitor_essence_status_message_property_id, nmos::fields::nc::essence_status_message, U("NcString"), true, true, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Essence status property transition counters"), nc_sender_monitor_essence_status_transition_counter_property_id, nmos::fields::nc::essence_status_transition_counter, U("NcUint64"), true, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Automatic reset counters and messages property (default: true)"), nc_sender_monitor_auto_reset_monitor_property_id, nmos::fields::nc::auto_reset_monitor, U("NcBoolean"), false, false, false, false, value::null())); return properties; } - web::json::value make_nc_sender_monitor_methods() + web::json::value make_sender_monitor_methods() { using web::json::value; auto methods = value::array(); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Gets the transmission error counters"), nc_sender_monitor_get_transmission_error_counters_method_id, U("GetTransmissionErrorCounters"), U("NcMethodResultCounters"), value::array(), false)); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Resets ALL counters"), nc_sender_monitor_reset_monitor_method_id, U("ResetCountersAndMessages"), U("NcMethodResult"), value::array(), false)); + web::json::push_back(methods, details::make_method_descriptor(U("Gets the transmission error counters"), nc_sender_monitor_get_transmission_error_counters_method_id, U("GetTransmissionErrorCounters"), U("NcMethodResultCounters"), value::array(), false)); + web::json::push_back(methods, details::make_method_descriptor(U("Resets ALL counters"), nc_sender_monitor_reset_monitor_method_id, U("ResetCountersAndMessages"), U("NcMethodResult"), value::array(), false)); return methods; } - web::json::value make_nc_sender_monitor_events() + web::json::value make_sender_monitor_events() { using web::json::value; @@ -1433,22 +1433,22 @@ namespace nmos } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon - web::json::value make_nc_ident_beacon_properties() + web::json::value make_ident_beacon_properties() { using web::json::value; auto properties = value::array(); - web::json::push_back(properties, details::make_nc_property_descriptor(U("Indicator active state"), nc_ident_beacon_active_property_id, nmos::fields::nc::active, U("NcBoolean"), false, false, false, false, value::null())); + web::json::push_back(properties, details::make_property_descriptor(U("Indicator active state"), nc_ident_beacon_active_property_id, nmos::fields::nc::active, U("NcBoolean"), false, false, false, false, value::null())); return properties; } - web::json::value make_nc_ident_beacon_methods() + web::json::value make_ident_beacon_methods() { using web::json::value; return value::array(); } - web::json::value make_nc_ident_beacon_events() + web::json::value make_ident_beacon_events() { using web::json::value; @@ -1458,44 +1458,44 @@ namespace nmos // Device configuration classes // NcBulkPropertiesManager // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager - web::json::value make_nc_bulk_properties_manager_properties() + web::json::value make_bulk_properties_manager_properties() { using web::json::value; return value::array(); } - web::json::value make_nc_bulk_properties_manager_methods() + web::json::value make_bulk_properties_manager_methods() { using web::json::value; auto methods = value::array(); { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The target role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will return properties on specified path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true, property holders returned will contain non-null property descriptors and for full backups the ClassManager role path will also be included"), nmos::fields::nc::include_descriptors, U("NcBoolean"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Get bulk object properties by given path"), nc_bulk_properties_manager_get_properties_by_path_method_id, U("GetPropertiesByPath"), U("NcMethodResultBulkPropertiesHolder"), parameters, false)); + web::json::push_back(parameters, details::make_parameter_descriptor(U("The target role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("If true will return properties on specified path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("If true, property holders returned will contain non-null property descriptors and for full backups the ClassManager role path will also be included"), nmos::fields::nc::include_descriptors, U("NcBoolean"), false, false, value::null())); + web::json::push_back(methods, details::make_method_descriptor(U("Get bulk object properties by given path"), nc_bulk_properties_manager_get_properties_by_path_method_id, U("GetPropertiesByPath"), U("NcMethodResultBulkPropertiesHolder"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkPropertiesHolder"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Defines the restore mode to be applied"), nmos::fields::nc::restore_mode, U("NcRestoreMode"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Validate bulk properties for setting by given paths"), nc_bulk_properties_manager_validate_set_properties_by_path_method_id, U("ValidateSetPropertiesByPath"), U("NcMethodResultObjectPropertiesSetValidation"), parameters, false)); + web::json::push_back(parameters, details::make_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkPropertiesHolder"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Defines the restore mode to be applied"), nmos::fields::nc::restore_mode, U("NcRestoreMode"), false, false, value::null())); + web::json::push_back(methods, details::make_method_descriptor(U("Validate bulk properties for setting by given paths"), nc_bulk_properties_manager_validate_set_properties_by_path_method_id, U("ValidateSetPropertiesByPath"), U("NcMethodResultObjectPropertiesSetValidation"), parameters, false)); } { auto parameters = value::array(); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkPropertiesHolder"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); - web::json::push_back(parameters, details::make_nc_parameter_descriptor(U("Defines the restore mode to be applied"), nmos::fields::nc::restore_mode, U("NcRestoreMode"), false, false, value::null())); - web::json::push_back(methods, details::make_nc_method_descriptor(U("Set bulk properties for setting by given paths"), nc_bulk_properties_manager_set_properties_by_path_method_id, U("SetPropertiesByPath"), U("NcMethodResultObjectPropertiesSetValidation"), parameters, false)); + web::json::push_back(parameters, details::make_parameter_descriptor(U("The values offered (this may include read-only values and also paths which are not the target role path)"), nmos::fields::nc::data_set, U("NcBulkPropertiesHolder"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("If set the descriptor would contain all inherited elements"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("If true will validate properties on target path and all the nested paths"), nmos::fields::nc::recurse, U("NcBoolean"), false, false, value::null())); + web::json::push_back(parameters, details::make_parameter_descriptor(U("Defines the restore mode to be applied"), nmos::fields::nc::restore_mode, U("NcRestoreMode"), false, false, value::null())); + web::json::push_back(methods, details::make_method_descriptor(U("Set bulk properties for setting by given paths"), nc_bulk_properties_manager_set_properties_by_path_method_id, U("SetPropertiesByPath"), U("NcMethodResultObjectPropertiesSetValidation"), parameters, false)); } return methods; } - web::json::value make_nc_bulk_properties_manager_events() + web::json::value make_bulk_properties_manager_events() { using web::json::value; @@ -1503,1041 +1503,1041 @@ namespace nmos } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.html - web::json::value make_nc_object_class() + web::json::value make_object_class() { using web::json::value; - return details::make_nc_class_descriptor(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), make_nc_object_properties(), make_nc_object_methods(), make_nc_object_events()); + return details::make_class_descriptor(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), make_object_properties(), make_object_methods(), make_object_events()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.1.html - web::json::value make_nc_block_class() + web::json::value make_block_class() { using web::json::value; - return details::make_nc_class_descriptor(U("NcBlock class descriptor"), nc_block_class_id, U("NcBlock"), make_nc_block_properties(), make_nc_block_methods(), make_nc_block_events()); + return details::make_class_descriptor(U("NcBlock class descriptor"), nc_block_class_id, U("NcBlock"), make_block_properties(), make_block_methods(), make_block_events()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.2.html - web::json::value make_nc_worker_class() + web::json::value make_worker_class() { using web::json::value; - return details::make_nc_class_descriptor(U("NcWorker class descriptor"), nc_worker_class_id, U("NcWorker"), make_nc_worker_properties(), make_nc_worker_methods(), make_nc_worker_events()); + return details::make_class_descriptor(U("NcWorker class descriptor"), nc_worker_class_id, U("NcWorker"), make_worker_properties(), make_worker_methods(), make_worker_events()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.html - web::json::value make_nc_manager_class() + web::json::value make_manager_class() { using web::json::value; - return details::make_nc_class_descriptor(U("NcManager class descriptor"), nc_manager_class_id, U("NcManager"), make_nc_manager_properties(), make_nc_manager_methods(), make_nc_manager_events()); + return details::make_class_descriptor(U("NcManager class descriptor"), nc_manager_class_id, U("NcManager"), make_manager_properties(), make_manager_methods(), make_manager_events()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.1.html - web::json::value make_nc_device_manager_class() + web::json::value make_device_manager_class() { using web::json::value; - return details::make_nc_class_descriptor(U("NcDeviceManager class descriptor"), nc_device_manager_class_id, U("NcDeviceManager"), U("DeviceManager"), make_nc_device_manager_properties(), make_nc_device_manager_methods(), make_nc_device_manager_events()); + return details::make_class_descriptor(U("NcDeviceManager class descriptor"), nc_device_manager_class_id, U("NcDeviceManager"), U("DeviceManager"), make_device_manager_properties(), make_device_manager_methods(), make_device_manager_events()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.2.html - web::json::value make_nc_class_manager_class() + web::json::value make_class_manager_class() { using web::json::value; - return details::make_nc_class_descriptor(U("NcClassManager class descriptor"), nc_class_manager_class_id, U("NcClassManager"), U("ClassManager"), make_nc_class_manager_properties(), make_nc_class_manager_methods(), make_nc_class_manager_events()); + return details::make_class_descriptor(U("NcClassManager class descriptor"), nc_class_manager_class_id, U("NcClassManager"), U("ClassManager"), make_class_manager_properties(), make_class_manager_methods(), make_class_manager_events()); } // Identification feature set control classes // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon - web::json::value make_nc_ident_beacon_class() + web::json::value make_ident_beacon_class() { using web::json::value; - return details::make_nc_class_descriptor(U("NcIdentBeacon class descriptor"), nc_ident_beacon_class_id, U("NcIdentBeacon"), make_nc_ident_beacon_properties(), make_nc_ident_beacon_methods(), make_nc_ident_beacon_events()); + return details::make_class_descriptor(U("NcIdentBeacon class descriptor"), nc_ident_beacon_class_id, U("NcIdentBeacon"), make_ident_beacon_properties(), make_ident_beacon_methods(), make_ident_beacon_events()); } // Monitoring feature set control classes // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstatusmonitor - web::json::value make_nc_status_monitor_class() + web::json::value make_status_monitor_class() { using web::json::value; - return details::make_nc_class_descriptor(U("NcStatusMonitor class descriptor"), nc_status_monitor_class_id, U("NcStatusMonitor"), make_nc_status_monitor_properties(), make_nc_status_monitor_methods(), make_nc_status_monitor_events()); + return details::make_class_descriptor(U("NcStatusMonitor class descriptor"), nc_status_monitor_class_id, U("NcStatusMonitor"), make_status_monitor_properties(), make_status_monitor_methods(), make_status_monitor_events()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor - web::json::value make_nc_receiver_monitor_class() + web::json::value make_receiver_monitor_class() { using web::json::value; - return details::make_nc_class_descriptor(U("NcReceiverMonitor class descriptor"), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), make_nc_receiver_monitor_properties(), make_nc_receiver_monitor_methods(), make_nc_receiver_monitor_events()); + return details::make_class_descriptor(U("NcReceiverMonitor class descriptor"), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), make_receiver_monitor_properties(), make_receiver_monitor_methods(), make_receiver_monitor_events()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsendermonitor - web::json::value make_nc_sender_monitor_class() + web::json::value make_sender_monitor_class() { using web::json::value; - return details::make_nc_class_descriptor(U("NcSenderMonitor class descriptor"), nc_sender_monitor_class_id, U("NcSenderMonitor"), make_nc_sender_monitor_properties(), make_nc_sender_monitor_methods(), make_nc_sender_monitor_events()); + return details::make_class_descriptor(U("NcSenderMonitor class descriptor"), nc_sender_monitor_class_id, U("NcSenderMonitor"), make_sender_monitor_properties(), make_sender_monitor_methods(), make_sender_monitor_events()); } // Device configuration feature set control classes // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager - web::json::value make_nc_bulk_properties_manager_class() + web::json::value make_bulk_properties_manager_class() { using web::json::value; - return details::make_nc_class_descriptor(U("NcBulkPropertiesManager class descriptor"), nc_bulk_properties_manager_class_id, U("NcBulkPropertiesManager"), U("BulkPropertiesManager"), make_nc_bulk_properties_manager_properties(), make_nc_bulk_properties_manager_methods(), make_nc_bulk_properties_manager_events()); + return details::make_class_descriptor(U("NcBulkPropertiesManager class descriptor"), nc_bulk_properties_manager_class_id, U("NcBulkPropertiesManager"), U("BulkPropertiesManager"), make_bulk_properties_manager_properties(), make_bulk_properties_manager_methods(), make_bulk_properties_manager_events()); } // Primitive datatypes // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_boolean_datatype() + web::json::value make_boolean_datatype() { using web::json::value; - return details::make_nc_datatype_descriptor_primitive(U("Boolean primitive type"), U("NcBoolean"), value::null()); + return details::make_datatype_descriptor_primitive(U("Boolean primitive type"), U("NcBoolean"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_int16_datatype() + web::json::value make_int16_datatype() { using web::json::value; - return details::make_nc_datatype_descriptor_primitive(U("short"), U("NcInt16"), value::null()); + return details::make_datatype_descriptor_primitive(U("short"), U("NcInt16"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_int32_datatype() + web::json::value make_int32_datatype() { using web::json::value; - return details::make_nc_datatype_descriptor_primitive(U("long"), U("NcInt32"), value::null()); + return details::make_datatype_descriptor_primitive(U("long"), U("NcInt32"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_int64_datatype() + web::json::value make_int64_datatype() { using web::json::value; - return details::make_nc_datatype_descriptor_primitive(U("longlong"), U("NcInt64"), value::null()); + return details::make_datatype_descriptor_primitive(U("longlong"), U("NcInt64"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_uint16_datatype() + web::json::value make_uint16_datatype() { using web::json::value; - return details::make_nc_datatype_descriptor_primitive(U("unsignedshort"), U("NcUint16"), value::null()); + return details::make_datatype_descriptor_primitive(U("unsignedshort"), U("NcUint16"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_uint32_datatype() + web::json::value make_uint32_datatype() { using web::json::value; - return details::make_nc_datatype_descriptor_primitive(U("unsignedlong"), U("NcUint32"), value::null()); + return details::make_datatype_descriptor_primitive(U("unsignedlong"), U("NcUint32"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_uint64_datatype() + web::json::value make_uint64_datatype() { using web::json::value; - return details::make_nc_datatype_descriptor_primitive(U("unsignedlonglong"), U("NcUint64"), value::null()); + return details::make_datatype_descriptor_primitive(U("unsignedlonglong"), U("NcUint64"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_float32_datatype() + web::json::value make_float32_datatype() { using web::json::value; - return details::make_nc_datatype_descriptor_primitive(U("unrestrictedfloat"), U("NcFloat32"), value::null()); + return details::make_datatype_descriptor_primitive(U("unrestrictedfloat"), U("NcFloat32"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_float64_datatype() + web::json::value make_float64_datatype() { using web::json::value; - return details::make_nc_datatype_descriptor_primitive(U("unrestricteddouble"), U("NcFloat64"), value::null()); + return details::make_datatype_descriptor_primitive(U("unrestricteddouble"), U("NcFloat64"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_string_datatype() + web::json::value make_string_datatype() { using web::json::value; - return details::make_nc_datatype_descriptor_primitive(U("UTF-8 string"), U("NcString"), value::null()); + return details::make_datatype_descriptor_primitive(U("UTF-8 string"), U("NcString"), value::null()); } // Standard datatypes // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcBlockMemberDescriptor.html - web::json::value make_nc_block_member_descriptor_datatype() + web::json::value make_block_member_descriptor_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Role of member in its containing block"), nmos::fields::nc::role, U("NcString"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("OID of member"), nmos::fields::nc::oid, U("NcOid"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff member's OID is hardwired into device"), nmos::fields::nc::constant_oid, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Class ID"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("User label"), nmos::fields::nc::user_label, U("NcString"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Containing block's OID"), nmos::fields::nc::owner, U("NcOid"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Descriptor which is specific to a block member"), U("NcBlockMemberDescriptor"), fields, U("NcDescriptor"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Role of member in its containing block"), nmos::fields::nc::role, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("OID of member"), nmos::fields::nc::oid, U("NcOid"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("TRUE iff member's OID is hardwired into device"), nmos::fields::nc::constant_oid, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Class ID"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("User label"), nmos::fields::nc::user_label, U("NcString"), true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Containing block's OID"), nmos::fields::nc::owner, U("NcOid"), false, false, value::null())); + return details::make_datatype_descriptor_struct(U("Descriptor which is specific to a block member"), U("NcBlockMemberDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassDescriptor.html - web::json::value make_nc_class_descriptor_datatype() + web::json::value make_class_descriptor_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Identity of the class"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of the class"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Role if the class has fixed role (manager classes)"), nmos::fields::nc::fixed_role, U("NcString"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property descriptors"), nmos::fields::nc::properties, U("NcPropertyDescriptor"), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Method descriptors"), nmos::fields::nc::methods, U("NcMethodDescriptor"), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Event descriptors"), nmos::fields::nc::events, U("NcEventDescriptor"), false, true, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Descriptor of a class"), U("NcClassDescriptor"), fields, U("NcDescriptor"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Identity of the class"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Name of the class"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Role if the class has fixed role (manager classes)"), nmos::fields::nc::fixed_role, U("NcString"), true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Property descriptors"), nmos::fields::nc::properties, U("NcPropertyDescriptor"), false, true, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Method descriptors"), nmos::fields::nc::methods, U("NcMethodDescriptor"), false, true, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Event descriptors"), nmos::fields::nc::events, U("NcEventDescriptor"), false, true, value::null())); + return details::make_datatype_descriptor_struct(U("Descriptor of a class"), U("NcClassDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassId.html - web::json::value make_nc_class_id_datatype() + web::json::value make_class_id_datatype() { using web::json::value; - return details::make_nc_datatype_typedef(U("Sequence of class ID fields"), U("NcClassId"), true, U("NcInt32"), value::null()); + return details::make_datatype_typedef(U("Sequence of class ID fields"), U("NcClassId"), true, U("NcInt32"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptor.html - web::json::value make_nc_datatype_descriptor_datatype() + web::json::value make_datatype_descriptor_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Datatype name"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Type: Primitive, Typedef, Struct, Enum"), nmos::fields::nc::type, U("NcDatatypeType"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Base datatype descriptor"), U("NcDatatypeDescriptor"), fields, U("NcDescriptor"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Datatype name"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Type: Primitive, Typedef, Struct, Enum"), nmos::fields::nc::type, U("NcDatatypeType"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); + return details::make_datatype_descriptor_struct(U("Base datatype descriptor"), U("NcDatatypeDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorEnum.html - web::json::value make_nc_datatype_descriptor_enum_datatype() + web::json::value make_datatype_descriptor_enum_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("One item descriptor per enum option"), nmos::fields::nc::items, U("NcEnumItemDescriptor"), false, true, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Enum datatype descriptor"), U("NcDatatypeDescriptorEnum"), fields, U("NcDatatypeDescriptor"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("One item descriptor per enum option"), nmos::fields::nc::items, U("NcEnumItemDescriptor"), false, true, value::null())); + return details::make_datatype_descriptor_struct(U("Enum datatype descriptor"), U("NcDatatypeDescriptorEnum"), fields, U("NcDatatypeDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorPrimitive.html - web::json::value make_nc_datatype_descriptor_primitive_datatype() + web::json::value make_datatype_descriptor_primitive_datatype() { using web::json::value; auto fields = value::array(); - return details::make_nc_datatype_descriptor_struct(U("Primitive datatype descriptor"), U("NcDatatypeDescriptorPrimitive"), fields, U("NcDatatypeDescriptor"), value::null()); + return details::make_datatype_descriptor_struct(U("Primitive datatype descriptor"), U("NcDatatypeDescriptorPrimitive"), fields, U("NcDatatypeDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorStruct.html - web::json::value make_nc_datatype_descriptor_struct_datatype() + web::json::value make_datatype_descriptor_struct_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("One item descriptor per field of the struct"), nmos::fields::nc::fields, U("NcFieldDescriptor"), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of the parent type if any or null if it has no parent"), nmos::fields::nc::parent_type, U("NcName"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Struct datatype descriptor"), U("NcDatatypeDescriptorStruct"), fields, U("NcDatatypeDescriptor"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("One item descriptor per field of the struct"), nmos::fields::nc::fields, U("NcFieldDescriptor"), false, true, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Name of the parent type if any or null if it has no parent"), nmos::fields::nc::parent_type, U("NcName"), true, false, value::null())); + return details::make_datatype_descriptor_struct(U("Struct datatype descriptor"), U("NcDatatypeDescriptorStruct"), fields, U("NcDatatypeDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorTypeDef.html - web::json::value make_nc_datatype_descriptor_type_def_datatype() + web::json::value make_datatype_descriptor_type_def_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Original typedef datatype name"), nmos::fields::nc::parent_type, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff type is a typedef sequence of another type"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Type def datatype descriptor"), U("NcDatatypeDescriptorTypeDef"), fields, U("NcDatatypeDescriptor"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Original typedef datatype name"), nmos::fields::nc::parent_type, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("TRUE iff type is a typedef sequence of another type"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); + return details::make_datatype_descriptor_struct(U("Type def datatype descriptor"), U("NcDatatypeDescriptorTypeDef"), fields, U("NcDatatypeDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeType.html - web::json::value make_nc_datatype_type_datatype() + web::json::value make_datatype_type_datatype() { using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Primitive datatype"), U("Primitive"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Simple alias of another datatype"), U("Typedef"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Data structure"), U("Struct"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Enum datatype"), U("Enum"), 3)); - return details::make_nc_datatype_descriptor_enum(U("Datatype type"), U("NcDatatypeType"), items, value::null()); + web::json::push_back(items, details::make_enum_item_descriptor(U("Primitive datatype"), U("Primitive"), 0)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Simple alias of another datatype"), U("Typedef"), 1)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Data structure"), U("Struct"), 2)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Enum datatype"), U("Enum"), 3)); + return details::make_datatype_descriptor_enum(U("Datatype type"), U("NcDatatypeType"), items, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDescriptor.html - web::json::value make_nc_descriptor_datatype() + web::json::value make_descriptor_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional user facing description"), nmos::fields::nc::description, U("NcString"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Base descriptor"), U("NcDescriptor"), fields, value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Optional user facing description"), nmos::fields::nc::description, U("NcString"), true, false, value::null())); + return details::make_datatype_descriptor_struct(U("Base descriptor"), U("NcDescriptor"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceGenericState.html - web::json::value make_nc_device_generic_state_datatype() + web::json::value make_device_generic_state_datatype() { using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Unknown"), U("Unknown"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Normal operation"), U("NormalOperation"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Device is initializing"), U("Initializing"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Device is performing a software or firmware update"), U("Updating"), 3)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Device is experiencing a licensing error"), U("LicensingError"), 4)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Device is experiencing an internal error"), U("InternalError"), 5)); - return details::make_nc_datatype_descriptor_enum(U("Device generic operational state"), U("NcDeviceGenericState"), items, value::null()); + web::json::push_back(items, details::make_enum_item_descriptor(U("Unknown"), U("Unknown"), 0)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Normal operation"), U("NormalOperation"), 1)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Device is initializing"), U("Initializing"), 2)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Device is performing a software or firmware update"), U("Updating"), 3)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Device is experiencing a licensing error"), U("LicensingError"), 4)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Device is experiencing an internal error"), U("InternalError"), 5)); + return details::make_datatype_descriptor_enum(U("Device generic operational state"), U("NcDeviceGenericState"), items, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceOperationalState.html - web::json::value make_nc_device_operational_state_datatype() + web::json::value make_device_operational_state_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Generic operational state"), nmos::fields::nc::generic_state, U("NcDeviceGenericState"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Specific device details"), nmos::fields::nc::device_specific_details, U("NcString"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Device operational state"), U("NcDeviceOperationalState"), fields, value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Generic operational state"), nmos::fields::nc::generic_state, U("NcDeviceGenericState"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Specific device details"), nmos::fields::nc::device_specific_details, U("NcString"), true, false, value::null())); + return details::make_datatype_descriptor_struct(U("Device operational state"), U("NcDeviceOperationalState"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcElementId.html - web::json::value make_nc_element_id_datatype() + web::json::value make_element_id_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Level of the element"), nmos::fields::nc::level, U("NcUint16"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Index of the element"), nmos::fields::nc::index, U("NcUint16"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Class element id which contains the level and index"), U("NcElementId"), fields, value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Level of the element"), nmos::fields::nc::level, U("NcUint16"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Index of the element"), nmos::fields::nc::index, U("NcUint16"), false, false, value::null())); + return details::make_datatype_descriptor_struct(U("Class element id which contains the level and index"), U("NcElementId"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEnumItemDescriptor.html - web::json::value make_nc_enum_item_descriptor_datatype() + web::json::value make_enum_item_descriptor_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of option"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Enum item numerical value"), nmos::fields::nc::value, U("NcUint16"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Descriptor of an enum item"), U("NcEnumItemDescriptor"), fields, U("NcDescriptor"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Name of option"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Enum item numerical value"), nmos::fields::nc::value, U("NcUint16"), false, false, value::null())); + return details::make_datatype_descriptor_struct(U("Descriptor of an enum item"), U("NcEnumItemDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventDescriptor.html - web::json::value make_nc_event_descriptor_datatype() + web::json::value make_event_descriptor_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Event id with level and index"), nmos::fields::nc::id, U("NcEventId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of event"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of event data's datatype"), nmos::fields::nc::event_datatype, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is marked as deprecated"), nmos::fields::nc::is_deprecated, U("NcBoolean"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Descriptor of a class event"), U("NcEventDescriptor"), fields, U("NcDescriptor"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Event id with level and index"), nmos::fields::nc::id, U("NcEventId"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Name of event"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Name of event data's datatype"), nmos::fields::nc::event_datatype, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("TRUE iff property is marked as deprecated"), nmos::fields::nc::is_deprecated, U("NcBoolean"), false, false, value::null())); + return details::make_datatype_descriptor_struct(U("Descriptor of a class event"), U("NcEventDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventId.html - web::json::value make_nc_event_id_datatype() + web::json::value make_event_id_datatype() { using web::json::value; - return details::make_nc_datatype_descriptor_struct(U("Event id which contains the level and index"), U("NcEventId"), value::array(), U("NcElementId"), value::null()); + return details::make_datatype_descriptor_struct(U("Event id which contains the level and index"), U("NcEventId"), value::array(), U("NcElementId"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcFieldDescriptor.html - web::json::value make_nc_field_descriptor_datatype() + web::json::value make_field_descriptor_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of field"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of field's datatype. Can only ever be null if the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff field is nullable"), nmos::fields::nc::is_nullable, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff field is a sequence"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Descriptor of a field of a struct"), U("NcFieldDescriptor"), fields, U("NcDescriptor"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Name of field"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Name of field's datatype. Can only ever be null if the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("TRUE iff field is nullable"), nmos::fields::nc::is_nullable, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("TRUE iff field is a sequence"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); + return details::make_datatype_descriptor_struct(U("Descriptor of a field of a struct"), U("NcFieldDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcId.html - web::json::value make_nc_id_datatype() + web::json::value make_id_datatype() { using web::json::value; - return details::make_nc_datatype_typedef(U("Identity handler"), U("NcId"), false, U("NcUint32"), value::null()); + return details::make_datatype_typedef(U("Identity handler"), U("NcId"), false, U("NcUint32"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcManufacturer.html - web::json::value make_nc_manufacturer_datatype() + web::json::value make_manufacturer_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Manufacturer's name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("IEEE OUI or CID of manufacturer"), nmos::fields::nc::organization_id, U("NcOrganizationId"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("URL of the manufacturer's website"), nmos::fields::nc::website, U("NcUri"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Manufacturer descriptor"), U("NcManufacturer"), fields, value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Manufacturer's name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("IEEE OUI or CID of manufacturer"), nmos::fields::nc::organization_id, U("NcOrganizationId"), true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("URL of the manufacturer's website"), nmos::fields::nc::website, U("NcUri"), true, false, value::null())); + return details::make_datatype_descriptor_struct(U("Manufacturer descriptor"), U("NcManufacturer"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodDescriptor.html - web::json::value make_nc_method_descriptor_datatype() + web::json::value make_method_descriptor_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Method id with level and index"), nmos::fields::nc::id, U("NcMethodId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of method"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of method result's datatype"), nmos::fields::nc::result_datatype, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Parameter descriptors if any"), nmos::fields::nc::parameters, U("NcParameterDescriptor"), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is marked as deprecated"), nmos::fields::nc::is_deprecated, U("NcBoolean"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Descriptor of a class method"), U("NcMethodDescriptor"), fields, U("NcDescriptor"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Method id with level and index"), nmos::fields::nc::id, U("NcMethodId"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Name of method"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Name of method result's datatype"), nmos::fields::nc::result_datatype, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Parameter descriptors if any"), nmos::fields::nc::parameters, U("NcParameterDescriptor"), false, true, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("TRUE iff property is marked as deprecated"), nmos::fields::nc::is_deprecated, U("NcBoolean"), false, false, value::null())); + return details::make_datatype_descriptor_struct(U("Descriptor of a class method"), U("NcMethodDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodId.html - web::json::value make_nc_method_id_datatype() + web::json::value make_method_id_datatype() { using web::json::value; - return details::make_nc_datatype_descriptor_struct(U("Method id which contains the level and index"), U("NcMethodId"), value::array(), U("NcElementId"), value::null()); + return details::make_datatype_descriptor_struct(U("Method id which contains the level and index"), U("NcMethodId"), value::array(), U("NcElementId"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResult.html - web::json::value make_nc_method_result_datatype() + web::json::value make_method_result_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Status for the invoked method"), nmos::fields::nc::status, U("NcMethodStatus"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Base result of the invoked method"), U("NcMethodResult"), fields, value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Status for the invoked method"), nmos::fields::nc::status, U("NcMethodStatus"), false, false, value::null())); + return details::make_datatype_descriptor_struct(U("Base result of the invoked method"), U("NcMethodResult"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultBlockMemberDescriptors.html - web::json::value make_nc_method_result_block_member_descriptors_datatype() + web::json::value make_method_result_block_member_descriptors_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Block member descriptors method result value"), nmos::fields::nc::value, U("NcBlockMemberDescriptor"), false, true, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Method result containing block member descriptors as the value"), U("NcMethodResultBlockMemberDescriptors"), fields, U("NcMethodResult"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Block member descriptors method result value"), nmos::fields::nc::value, U("NcBlockMemberDescriptor"), false, true, value::null())); + return details::make_datatype_descriptor_struct(U("Method result containing block member descriptors as the value"), U("NcMethodResultBlockMemberDescriptors"), fields, U("NcMethodResult"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultClassDescriptor.html - web::json::value make_nc_method_result_class_descriptor_datatype() + web::json::value make_method_result_class_descriptor_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Class descriptor method result value"), nmos::fields::nc::value, U("NcClassDescriptor"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Method result containing a class descriptor as the value"), U("NcMethodResultClassDescriptor"), fields, U("NcMethodResult"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Class descriptor method result value"), nmos::fields::nc::value, U("NcClassDescriptor"), false, false, value::null())); + return details::make_datatype_descriptor_struct(U("Method result containing a class descriptor as the value"), U("NcMethodResultClassDescriptor"), fields, U("NcMethodResult"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultDatatypeDescriptor.html - web::json::value make_nc_method_result_datatype_descriptor_datatype() + web::json::value make_method_result_datatype_descriptor_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Datatype descriptor method result value"), nmos::fields::nc::value, U("NcDatatypeDescriptor"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Method result containing a datatype descriptor as the value"), U("NcMethodResultDatatypeDescriptor"), fields, U("NcMethodResult"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Datatype descriptor method result value"), nmos::fields::nc::value, U("NcDatatypeDescriptor"), false, false, value::null())); + return details::make_datatype_descriptor_struct(U("Method result containing a datatype descriptor as the value"), U("NcMethodResultDatatypeDescriptor"), fields, U("NcMethodResult"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultError.html - web::json::value make_nc_method_result_error_datatype() + web::json::value make_method_result_error_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Error message"), nmos::fields::nc::error_message, U("NcString"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Error result - to be used when the method call encounters an error"), U("NcMethodResultError"), fields, U("NcMethodResult"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Error message"), nmos::fields::nc::error_message, U("NcString"), false, false, value::null())); + return details::make_datatype_descriptor_struct(U("Error result - to be used when the method call encounters an error"), U("NcMethodResultError"), fields, U("NcMethodResult"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultId.html - web::json::value make_nc_method_result_id_datatype() + web::json::value make_method_result_id_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Id result value"), nmos::fields::nc::value, U("NcId"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Id method result"), U("NcMethodResultId"), fields, U("NcMethodResult"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Id result value"), nmos::fields::nc::value, U("NcId"), false, false, value::null())); + return details::make_datatype_descriptor_struct(U("Id method result"), U("NcMethodResultId"), fields, U("NcMethodResult"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultLength.html - web::json::value make_nc_method_result_length_datatype() + web::json::value make_method_result_length_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Length result value"), nmos::fields::nc::value, U("NcUint32"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Length method result"), U("NcMethodResultLength"), fields, U("NcMethodResult"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Length result value"), nmos::fields::nc::value, U("NcUint32"), true, false, value::null())); + return details::make_datatype_descriptor_struct(U("Length method result"), U("NcMethodResultLength"), fields, U("NcMethodResult"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultPropertyValue.html - web::json::value make_nc_method_result_property_value_datatype() + web::json::value make_method_result_property_value_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Getter method value for the associated property"), nmos::fields::nc::value, true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Result when invoking the getter method associated with a property"), U("NcMethodResultPropertyValue"), fields, U("NcMethodResult"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Getter method value for the associated property"), nmos::fields::nc::value, true, false, value::null())); + return details::make_datatype_descriptor_struct(U("Result when invoking the getter method associated with a property"), U("NcMethodResultPropertyValue"), fields, U("NcMethodResult"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodStatus.html - web::json::value make_nc_method_status_datatype() + web::json::value make_method_status_datatype() { using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call was successful"), U("Ok"), 200)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call was successful but targeted property is deprecated"), U("PropertyDeprecated"), 298)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call was successful but method is deprecated"), U("MethodDeprecated"), 299)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Badly-formed command (e.g. the incoming command has invalid message encoding and cannot be parsed by the underlying protocol)"), U("BadCommandFormat"), 400)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Client is not authorized"), U("Unauthorized"), 401)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Command addresses a nonexistent object"), U("BadOid"), 404)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Attempt to change read-only state"), U("Readonly"), 405)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call is invalid in current operating context (e.g. attempting to invoke a method when the object is disabled)"), U("InvalidRequest"), 406)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("There is a conflict with the current state of the device"), U("Conflict"), 409)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Something was too big"), U("BufferOverflow"), 413)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Index is outside the available range"), U("IndexOutOfBounds"), 414)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method parameter does not meet expectations (e.g. attempting to invoke a method with an invalid type for one of its parameters)"), U("ParameterError"), 417)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Addressed object is locked"), U("Locked"), 423)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Internal device error"), U("DeviceError"), 500)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Addressed method is not implemented by the addressed object"), U("MethodNotImplemented"), 501)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Addressed property is not implemented by the addressed object"), U("PropertyNotImplemented"), 502)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("The device is not ready to handle any commands"), U("NotReady"), 503)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Method call did not finish within the allotted time"), U("Timeout"), 504)); - return details::make_nc_datatype_descriptor_enum(U("Method invokation status"), U("NcMethodStatus"), items, value::null()); + web::json::push_back(items, details::make_enum_item_descriptor(U("Method call was successful"), U("Ok"), 200)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Method call was successful but targeted property is deprecated"), U("PropertyDeprecated"), 298)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Method call was successful but method is deprecated"), U("MethodDeprecated"), 299)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Badly-formed command (e.g. the incoming command has invalid message encoding and cannot be parsed by the underlying protocol)"), U("BadCommandFormat"), 400)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Client is not authorized"), U("Unauthorized"), 401)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Command addresses a nonexistent object"), U("BadOid"), 404)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Attempt to change read-only state"), U("Readonly"), 405)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Method call is invalid in current operating context (e.g. attempting to invoke a method when the object is disabled)"), U("InvalidRequest"), 406)); + web::json::push_back(items, details::make_enum_item_descriptor(U("There is a conflict with the current state of the device"), U("Conflict"), 409)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Something was too big"), U("BufferOverflow"), 413)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Index is outside the available range"), U("IndexOutOfBounds"), 414)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Method parameter does not meet expectations (e.g. attempting to invoke a method with an invalid type for one of its parameters)"), U("ParameterError"), 417)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Addressed object is locked"), U("Locked"), 423)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Internal device error"), U("DeviceError"), 500)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Addressed method is not implemented by the addressed object"), U("MethodNotImplemented"), 501)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Addressed property is not implemented by the addressed object"), U("PropertyNotImplemented"), 502)); + web::json::push_back(items, details::make_enum_item_descriptor(U("The device is not ready to handle any commands"), U("NotReady"), 503)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Method call did not finish within the allotted time"), U("Timeout"), 504)); + return details::make_datatype_descriptor_enum(U("Method invokation status"), U("NcMethodStatus"), items, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcName.html - web::json::value make_nc_name_datatype() + web::json::value make_name_datatype() { using web::json::value; - return details::make_nc_datatype_typedef(U("Programmatically significant name, alphanumerics + underscore, no spaces"), U("NcName"), false, U("NcString"), value::null()); + return details::make_datatype_typedef(U("Programmatically significant name, alphanumerics + underscore, no spaces"), U("NcName"), false, U("NcString"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOid.html - web::json::value make_nc_oid_datatype() + web::json::value make_oid_datatype() { using web::json::value; - return details::make_nc_datatype_typedef(U("Object id"), U("NcOid"), false, U("NcUint32"), value::null()); + return details::make_datatype_typedef(U("Object id"), U("NcOid"), false, U("NcUint32"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOrganizationId.html - web::json::value make_nc_organization_id_datatype() + web::json::value make_organization_id_datatype() { using web::json::value; - return details::make_nc_datatype_typedef(U("Unique 24-bit organization id"), U("NcOrganizationId"), false, U("NcInt32"), value::null()); + return details::make_datatype_typedef(U("Unique 24-bit organization id"), U("NcOrganizationId"), false, U("NcInt32"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraints.html - web::json::value make_nc_parameter_constraints_datatype() + web::json::value make_parameter_constraints_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Default value"), nmos::fields::nc::default_value, true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Abstract parameter constraints class"), U("NcParameterConstraints"), fields, value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Default value"), nmos::fields::nc::default_value, true, false, value::null())); + return details::make_datatype_descriptor_struct(U("Abstract parameter constraints class"), U("NcParameterConstraints"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsNumber.html - web::json::value make_nc_parameter_constraints_number_datatype() + web::json::value make_parameter_constraints_number_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional minimum"), nmos::fields::nc::minimum, true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional maximum"), nmos::fields::nc::maximum, true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional step"), nmos::fields::nc::step, true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Number parameter constraints class"), U("NcParameterConstraintsNumber"), fields, U("NcParameterConstraints"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Optional minimum"), nmos::fields::nc::minimum, true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Optional maximum"), nmos::fields::nc::maximum, true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Optional step"), nmos::fields::nc::step, true, false, value::null())); + return details::make_datatype_descriptor_struct(U("Number parameter constraints class"), U("NcParameterConstraintsNumber"), fields, U("NcParameterConstraints"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsString.html - web::json::value make_nc_parameter_constraints_string_datatype() + web::json::value make_parameter_constraints_string_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Maximum characters allowed"), nmos::fields::nc::max_characters, U("NcUint32"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Regex pattern"), nmos::fields::nc::pattern, U("NcRegex"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("String parameter constraints class"), U("NcParameterConstraintsString"), fields, U("NcParameterConstraints"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Maximum characters allowed"), nmos::fields::nc::max_characters, U("NcUint32"), true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Regex pattern"), nmos::fields::nc::pattern, U("NcRegex"), true, false, value::null())); + return details::make_datatype_descriptor_struct(U("String parameter constraints class"), U("NcParameterConstraintsString"), fields, U("NcParameterConstraints"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterDescriptor.html - web::json::value make_nc_parameter_descriptor_datatype() + web::json::value make_parameter_descriptor_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of parameter"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of parameter's datatype. Can only ever be null if the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is nullable"), nmos::fields::nc::is_nullable, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is a sequence"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Descriptor of a method parameter"), U("NcParameterDescriptor"), fields, U("NcDescriptor"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Name of parameter"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Name of parameter's datatype. Can only ever be null if the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("TRUE iff property is nullable"), nmos::fields::nc::is_nullable, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("TRUE iff property is a sequence"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); + return details::make_datatype_descriptor_struct(U("Descriptor of a method parameter"), U("NcParameterDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcProduct.html - web::json::value make_nc_product_datatype() + web::json::value make_product_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Product name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Manufacturer's unique key to product - model number, SKU, etc"), nmos::fields::nc::key, U("NcString"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Manufacturer's product revision level code"), nmos::fields::nc::revision_level, U("NcString"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Brand name under which product is sold"), nmos::fields::nc::brand_name, U("NcString"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Unique UUID of product (not product instance)"), nmos::fields::nc::uuid, U("NcUuid"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Text description of product"), nmos::fields::nc::description, U("NcString"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Product descriptor"), U("NcProduct"), fields, value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Product name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Manufacturer's unique key to product - model number, SKU, etc"), nmos::fields::nc::key, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Manufacturer's product revision level code"), nmos::fields::nc::revision_level, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Brand name under which product is sold"), nmos::fields::nc::brand_name, U("NcString"), true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Unique UUID of product (not product instance)"), nmos::fields::nc::uuid, U("NcUuid"), true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Text description of product"), nmos::fields::nc::description, U("NcString"), true, false, value::null())); + return details::make_datatype_descriptor_struct(U("Product descriptor"), U("NcProduct"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangeType.html - web::json::value make_nc_property_change_type_datatype() + web::json::value make_property_change_type_datatype() { using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Current value changed"), U("ValueChanged"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Sequence item added"), U("SequenceItemAdded"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Sequence item changed"), U("SequenceItemChanged"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Sequence item removed"), U("SequenceItemRemoved"), 3)); - return details::make_nc_datatype_descriptor_enum(U("Type of property change"), U("NcPropertyChangeType"), items, value::null()); + web::json::push_back(items, details::make_enum_item_descriptor(U("Current value changed"), U("ValueChanged"), 0)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Sequence item added"), U("SequenceItemAdded"), 1)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Sequence item changed"), U("SequenceItemChanged"), 2)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Sequence item removed"), U("SequenceItemRemoved"), 3)); + return details::make_datatype_descriptor_enum(U("Type of property change"), U("NcPropertyChangeType"), items, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangedEventData.html - web::json::value make_nc_property_changed_event_data_datatype() + web::json::value make_property_changed_event_data_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("The id of the property that changed"), nmos::fields::nc::property_id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Information regarding the change type"), nmos::fields::nc::change_type, U("NcPropertyChangeType"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property-type specific value"), nmos::fields::nc::value, true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Index of sequence item if the property is a sequence"), nmos::fields::nc::sequence_item_index,U("NcId"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Payload of property-changed event"), U("NcPropertyChangedEventData"), fields, value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("The id of the property that changed"), nmos::fields::nc::property_id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Information regarding the change type"), nmos::fields::nc::change_type, U("NcPropertyChangeType"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Property-type specific value"), nmos::fields::nc::value, true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Index of sequence item if the property is a sequence"), nmos::fields::nc::sequence_item_index,U("NcId"), true, false, value::null())); + return details::make_datatype_descriptor_struct(U("Payload of property-changed event"), U("NcPropertyChangedEventData"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraints.html - web::json::value make_nc_property_contraints_datatype() + web::json::value make_property_contraints_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("The id of the property being constrained"), nmos::fields::nc::property_id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional default value"), nmos::fields::nc::default_value, true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Property constraints class"), U("NcPropertyConstraints"), fields, value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("The id of the property being constrained"), nmos::fields::nc::property_id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Optional default value"), nmos::fields::nc::default_value, true, false, value::null())); + return details::make_datatype_descriptor_struct(U("Property constraints class"), U("NcPropertyConstraints"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsNumber.html - web::json::value make_nc_property_constraints_number_datatype() + web::json::value make_property_constraints_number_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional minimum"), nmos::fields::nc::minimum, true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional maximum"), nmos::fields::nc::maximum, true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional step"), nmos::fields::nc::step, true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Number property constraints class"), U("NcPropertyConstraintsNumber"), fields, U("NcPropertyConstraints"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Optional minimum"), nmos::fields::nc::minimum, true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Optional maximum"), nmos::fields::nc::maximum, true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Optional step"), nmos::fields::nc::step, true, false, value::null())); + return details::make_datatype_descriptor_struct(U("Number property constraints class"), U("NcPropertyConstraintsNumber"), fields, U("NcPropertyConstraints"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsString.html - web::json::value make_nc_property_constraints_string_datatype() + web::json::value make_property_constraints_string_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Maximum characters allowed"), nmos::fields::nc::max_characters, U("NcUint32"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Regex pattern"), nmos::fields::nc::pattern, U("NcRegex"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("String property constraints class"), U("NcPropertyConstraintsString"), fields, U("NcPropertyConstraints"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Maximum characters allowed"), nmos::fields::nc::max_characters, U("NcUint32"), true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Regex pattern"), nmos::fields::nc::pattern, U("NcRegex"), true, false, value::null())); + return details::make_datatype_descriptor_struct(U("String property constraints class"), U("NcPropertyConstraintsString"), fields, U("NcPropertyConstraints"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyDescriptor.html - web::json::value make_nc_property_descriptor_datatype() + web::json::value make_property_descriptor_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property id with level and index"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of property"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Name of property's datatype. Can only ever be null if the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is read-only"), nmos::fields::nc::is_read_only, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is nullable"), nmos::fields::nc::is_nullable, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is a sequence"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("TRUE iff property is marked as deprecated"), nmos::fields::nc::is_deprecated, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Descriptor of a class property"), U("NcPropertyDescriptor"), fields, U("NcDescriptor"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Property id with level and index"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Name of property"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Name of property's datatype. Can only ever be null if the type is any"), nmos::fields::nc::type_name, U("NcName"), true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("TRUE iff property is read-only"), nmos::fields::nc::is_read_only, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("TRUE iff property is nullable"), nmos::fields::nc::is_nullable, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("TRUE iff property is a sequence"), nmos::fields::nc::is_sequence, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("TRUE iff property is marked as deprecated"), nmos::fields::nc::is_deprecated, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Optional constraints on top of the underlying data type"), nmos::fields::nc::constraints, U("NcParameterConstraints"), true, false, value::null())); + return details::make_datatype_descriptor_struct(U("Descriptor of a class property"), U("NcPropertyDescriptor"), fields, U("NcDescriptor"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyId.html - web::json::value make_nc_property_id_datatype() + web::json::value make_property_id_datatype() { using web::json::value; - return details::make_nc_datatype_descriptor_struct(U("Property id which contains the level and index"), U("NcPropertyId"), value::array(), U("NcElementId"), value::null()); + return details::make_datatype_descriptor_struct(U("Property id which contains the level and index"), U("NcPropertyId"), value::array(), U("NcElementId"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRegex.html - web::json::value make_nc_regex_datatype() + web::json::value make_regex_datatype() { using web::json::value; - return details::make_nc_datatype_typedef(U("Regex pattern"), U("NcRegex"), false, U("NcString"), value::null()); + return details::make_datatype_typedef(U("Regex pattern"), U("NcRegex"), false, U("NcString"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcResetCause.html - web::json::value make_nc_reset_cause_datatype() + web::json::value make_reset_cause_datatype() { using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Unknown"), U("Unknown"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Power on"), U("PowerOn"), 1)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Internal error"), U("InternalError"), 2)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Upgrade"), U("Upgrade"), 3)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Controller request"), U("ControllerRequest"), 4)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Manual request from the front panel"), U("ManualReset"), 5)); - return details::make_nc_datatype_descriptor_enum(U("Reset cause enum"), U("NcResetCause"), items, value::null()); + web::json::push_back(items, details::make_enum_item_descriptor(U("Unknown"), U("Unknown"), 0)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Power on"), U("PowerOn"), 1)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Internal error"), U("InternalError"), 2)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Upgrade"), U("Upgrade"), 3)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Controller request"), U("ControllerRequest"), 4)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Manual request from the front panel"), U("ManualReset"), 5)); + return details::make_datatype_descriptor_enum(U("Reset cause enum"), U("NcResetCause"), items, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRolePath.html - web::json::value make_nc_role_path_datatype() + web::json::value make_role_path_datatype() { using web::json::value; - return details::make_nc_datatype_typedef(U("Role path"), U("NcRolePath"), true, U("NcString"), value::null()); + return details::make_datatype_typedef(U("Role path"), U("NcRolePath"), true, U("NcString"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTimeInterval.html - web::json::value make_nc_time_interval_datatype() + web::json::value make_time_interval_datatype() { using web::json::value; - return details::make_nc_datatype_typedef(U("Time interval described in nanoseconds"), U("NcTimeInterval"), false, U("NcInt64"), value::null()); + return details::make_datatype_typedef(U("Time interval described in nanoseconds"), U("NcTimeInterval"), false, U("NcInt64"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpoint.html - web::json::value make_nc_touchpoint_datatype() + web::json::value make_touchpoint_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Context namespace"), nmos::fields::nc::context_namespace, U("NcString"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Base touchpoint class"), U("NcTouchpoint"), fields, value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Context namespace"), nmos::fields::nc::context_namespace, U("NcString"), false, false, value::null())); + return details::make_datatype_descriptor_struct(U("Base touchpoint class"), U("NcTouchpoint"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmos.html - web::json::value make_nc_touchpoint_nmos_datatype() + web::json::value make_touchpoint_nmos_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Context NMOS resource"), nmos::fields::nc::resource, U("NcTouchpointResourceNmos"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Touchpoint class for NMOS resources"), U("NcTouchpointNmos"), fields, U("NcTouchpoint"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Context NMOS resource"), nmos::fields::nc::resource, U("NcTouchpointResourceNmos"), false, false, value::null())); + return details::make_datatype_descriptor_struct(U("Touchpoint class for NMOS resources"), U("NcTouchpointNmos"), fields, U("NcTouchpoint"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmosChannelMapping.html - web::json::value make_nc_touchpoint_nmos_channel_mapping_datatype() + web::json::value make_touchpoint_nmos_channel_mapping_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Context Channel Mapping resource"), nmos::fields::nc::resource,U("NcTouchpointResourceNmosChannelMapping"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Touchpoint class for NMOS IS-08 resources"), U("NcTouchpointNmosChannelMapping"), fields, U("NcTouchpoint"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Context Channel Mapping resource"), nmos::fields::nc::resource,U("NcTouchpointResourceNmosChannelMapping"), false, false, value::null())); + return details::make_datatype_descriptor_struct(U("Touchpoint class for NMOS IS-08 resources"), U("NcTouchpointNmosChannelMapping"), fields, U("NcTouchpoint"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResource.html - web::json::value make_nc_touchpoint_resource_datatype() + web::json::value make_touchpoint_resource_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("The type of the resource"), nmos::fields::nc::resource_type, U("NcString"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Touchpoint resource class"), U("NcTouchpointResource"), fields, value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("The type of the resource"), nmos::fields::nc::resource_type, U("NcString"), false, false, value::null())); + return details::make_datatype_descriptor_struct(U("Touchpoint resource class"), U("NcTouchpointResource"), fields, value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmos.html - web::json::value make_nc_touchpoint_resource_nmos_datatype() + web::json::value make_touchpoint_resource_nmos_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("NMOS resource UUID"), nmos::fields::nc::id, U("NcUuid"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Touchpoint resource class for NMOS resources"), U("NcTouchpointResourceNmos"), fields, U("NcTouchpointResource"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("NMOS resource UUID"), nmos::fields::nc::id, U("NcUuid"), false, false, value::null())); + return details::make_datatype_descriptor_struct(U("Touchpoint resource class for NMOS resources"), U("NcTouchpointResourceNmos"), fields, U("NcTouchpointResource"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmosChannelMapping.html - web::json::value make_nc_touchpoint_resource_nmos_channel_mapping_datatype() + web::json::value make_touchpoint_resource_nmos_channel_mapping_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("IS-08 Audio Channel Mapping input or output id"), nmos::fields::nc::io_id, U("NcString"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Touchpoint resource class for NMOS resources"), U("NcTouchpointResourceNmosChannelMapping"), fields, U("NcTouchpointResourceNmos"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("IS-08 Audio Channel Mapping input or output id"), nmos::fields::nc::io_id, U("NcString"), false, false, value::null())); + return details::make_datatype_descriptor_struct(U("Touchpoint resource class for NMOS resources"), U("NcTouchpointResourceNmosChannelMapping"), fields, U("NcTouchpointResourceNmos"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUri.html - web::json::value make_nc_uri_datatype() + web::json::value make_uri_datatype() { using web::json::value; - return details::make_nc_datatype_typedef(U("Uniform resource identifier"), U("NcUri"), false, U("NcString"), value::null()); + return details::make_datatype_typedef(U("Uniform resource identifier"), U("NcUri"), false, U("NcString"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUuid.html - web::json::value make_nc_uuid_datatype() + web::json::value make_uuid_datatype() { using web::json::value; - return details::make_nc_datatype_typedef(U("UUID"), U("NcUuid"), false, U("NcString"), value::null()); + return details::make_datatype_typedef(U("UUID"), U("NcUuid"), false, U("NcString"), value::null()); } // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcVersionCode.html - web::json::value make_nc_version_code_datatype() + web::json::value make_version_code_datatype() { using web::json::value; - return details::make_nc_datatype_typedef(U("Version code in semantic versioning format"), U("NcVersionCode"), false, U("NcString"), value::null()); + return details::make_datatype_typedef(U("Version code in semantic versioning format"), U("NcVersionCode"), false, U("NcString"), value::null()); } // Monitoring datatype defintions // // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncconnectionstatus - web::json::value make_nc_connection_status_datatype() + web::json::value make_connection_status_datatype() { using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_connection_status::status::inactive)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and healthy"), U("Healthy"), nc_connection_status::status::healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and partially healthy"), U("PartiallyHealthy"), nc_connection_status::status::partially_healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and unhealthy"), U("Unhealthy"), nc_connection_status::status::unhealthy)); - return details::make_nc_datatype_descriptor_enum(U("Connection status enum data type"), U("NcConnectionStatus"), items, value::null()); + web::json::push_back(items, details::make_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_connection_status::status::inactive)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Active and healthy"), U("Healthy"), nc_connection_status::status::healthy)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Active and partially healthy"), U("PartiallyHealthy"), nc_connection_status::status::partially_healthy)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Active and unhealthy"), U("Unhealthy"), nc_connection_status::status::unhealthy)); + return details::make_datatype_descriptor_enum(U("Connection status enum data type"), U("NcConnectionStatus"), items, value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nccounter - web::json::value make_nc_counter_datatype() + web::json::value make_counter_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Counter name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Counter value"), nmos::fields::nc::value, U("NcUint64"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Description"), nmos::fields::nc::description, U("NcString"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Counter data type"), U("NcCounter"), fields, value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Counter name"), nmos::fields::nc::name, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Counter value"), nmos::fields::nc::value, U("NcUint64"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Description"), nmos::fields::nc::description, U("NcString"), true, false, value::null())); + return details::make_datatype_descriptor_struct(U("Counter data type"), U("NcCounter"), fields, value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncessencestatus - web::json::value make_nc_essence_status_datatype() + web::json::value make_essence_status_datatype() { using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_essence_status::status::inactive)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and healthy"), U("Healthy"), nc_essence_status::status::healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and partially healthy"), U("PartiallyHealthy"), nc_essence_status::status::partially_healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and unhealthy"), U("Unhealthy"), nc_essence_status::status::unhealthy)); - return details::make_nc_datatype_descriptor_enum(U("Essence status enum data type"), U("NcEssenceStatus"), items, value::null()); + web::json::push_back(items, details::make_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_essence_status::status::inactive)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Active and healthy"), U("Healthy"), nc_essence_status::status::healthy)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Active and partially healthy"), U("PartiallyHealthy"), nc_essence_status::status::partially_healthy)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Active and unhealthy"), U("Unhealthy"), nc_essence_status::status::unhealthy)); + return details::make_datatype_descriptor_enum(U("Essence status enum data type"), U("NcEssenceStatus"), items, value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nclinkstatus - web::json::value make_nc_link_status_datatype() + web::json::value make_link_status_datatype() { using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("All the associated network interfaces are up"), U("AllUp"), nc_link_status::status::all_up)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Some of the associated network interfaces are down"), U("SomeDown"), nc_link_status::status::some_down)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("All the associated network interfaces are down"), U("AllDown"), nc_link_status::status::all_down)); - return details::make_nc_datatype_descriptor_enum(U("Link status enum data type"), U("NcLinkStatus"), items, value::null()); + web::json::push_back(items, details::make_enum_item_descriptor(U("All the associated network interfaces are up"), U("AllUp"), nc_link_status::status::all_up)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Some of the associated network interfaces are down"), U("SomeDown"), nc_link_status::status::some_down)); + web::json::push_back(items, details::make_enum_item_descriptor(U("All the associated network interfaces are down"), U("AllDown"), nc_link_status::status::all_down)); + return details::make_datatype_descriptor_enum(U("Link status enum data type"), U("NcLinkStatus"), items, value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncoverallstatus - web::json::value make_nc_overall_status_datatype() + web::json::value make_overall_status_datatype() { using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_overall_status::status::inactive)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("The overall status is healthy"), U("Healthy"), nc_overall_status::status::healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("The overall status is partially healthy"), U("PartiallyHealthy"), nc_overall_status::status::partially_healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("The overall status is unhealthy"), U("Unhealthy"), nc_overall_status::status::unhealthy)); - return details::make_nc_datatype_descriptor_enum(U("Overall status enum data type"), U("NcOverallStatus"), items, value::null()); + web::json::push_back(items, details::make_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_overall_status::status::inactive)); + web::json::push_back(items, details::make_enum_item_descriptor(U("The overall status is healthy"), U("Healthy"), nc_overall_status::status::healthy)); + web::json::push_back(items, details::make_enum_item_descriptor(U("The overall status is partially healthy"), U("PartiallyHealthy"), nc_overall_status::status::partially_healthy)); + web::json::push_back(items, details::make_enum_item_descriptor(U("The overall status is unhealthy"), U("Unhealthy"), nc_overall_status::status::unhealthy)); + return details::make_datatype_descriptor_enum(U("Overall status enum data type"), U("NcOverallStatus"), items, value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsynchronizationstatus - web::json::value make_nc_synchronization_status_datatype() + web::json::value make_synchronization_status_datatype() { using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Feature not in use"), U("NotUsed"), nc_synchronization_status::status::not_used)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Locked to a synchronization source"), U("Healthy"), nc_synchronization_status::status::healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Partially locked to a synchronization source"), U("PartiallyHealthy"), nc_synchronization_status::status::partially_healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Not locked to a synchronization source"), U("Unhealthy"), nc_synchronization_status::status::unhealthy)); - return details::make_nc_datatype_descriptor_enum(U("Synchronization status enum data type"), U("NcSynchronizationStatus"), items, value::null()); + web::json::push_back(items, details::make_enum_item_descriptor(U("Feature not in use"), U("NotUsed"), nc_synchronization_status::status::not_used)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Locked to a synchronization source"), U("Healthy"), nc_synchronization_status::status::healthy)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Partially locked to a synchronization source"), U("PartiallyHealthy"), nc_synchronization_status::status::partially_healthy)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Not locked to a synchronization source"), U("Unhealthy"), nc_synchronization_status::status::unhealthy)); + return details::make_datatype_descriptor_enum(U("Synchronization status enum data type"), U("NcSynchronizationStatus"), items, value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstreamstatus - web::json::value make_nc_stream_status_datatype() + web::json::value make_stream_status_datatype() { using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_stream_status::status::inactive)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and healthy"), U("Healthy"), nc_stream_status::status::healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and partially healthy"), U("PartiallyHealthy"), nc_stream_status::status::partially_healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and unhealthy"), U("Unhealthy"), nc_stream_status::status::unhealthy)); - return details::make_nc_datatype_descriptor_enum(U("Stream status enum data type"), U("NcStreamStatus"), items, value::null()); + web::json::push_back(items, details::make_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_stream_status::status::inactive)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Active and healthy"), U("Healthy"), nc_stream_status::status::healthy)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Active and partially healthy"), U("PartiallyHealthy"), nc_stream_status::status::partially_healthy)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Active and unhealthy"), U("Unhealthy"), nc_stream_status::status::unhealthy)); + return details::make_datatype_descriptor_enum(U("Stream status enum data type"), U("NcStreamStatus"), items, value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nctransmissionstatus - web::json::value make_nc_transmission_status_datatype() + web::json::value make_transmission_status_datatype() { using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_transmission_status::status::inactive)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and healthy"), U("Healthy"), nc_transmission_status::status::healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and partially healthy"), U("PartiallyHealthy"), nc_transmission_status::status::partially_healthy)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Active and unhealthy"), U("Unhealthy"), nc_transmission_status::status::unhealthy)); - return details::make_nc_datatype_descriptor_enum(U("Transmission status enum data type"), U("NcTransmissionStatus"), items, value::null()); + web::json::push_back(items, details::make_enum_item_descriptor(U("Inactive"), U("Inactive"), nc_transmission_status::status::inactive)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Active and healthy"), U("Healthy"), nc_transmission_status::status::healthy)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Active and partially healthy"), U("PartiallyHealthy"), nc_transmission_status::status::partially_healthy)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Active and unhealthy"), U("Unhealthy"), nc_transmission_status::status::unhealthy)); + return details::make_datatype_descriptor_enum(U("Transmission status enum data type"), U("NcTransmissionStatus"), items, value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncmethodresultcounters - web::json::value make_nc_method_result_counters_datatype() + web::json::value make_method_result_counters_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Counters"), nmos::fields::nc::value, U("NcCounter"), false, true, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Counter method result"), U("NcMethodResultCounters"), fields, U("NcMethodResult"), value::null()); + web::json::push_back(fields, details::make_field_descriptor(U("Counters"), nmos::fields::nc::value, U("NcCounter"), false, true, value::null())); + return details::make_datatype_descriptor_struct(U("Counter method result"), U("NcMethodResultCounters"), fields, U("NcMethodResult"), value::null()); } // Device Configuration datatypes // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestoremode - web::json::value make_nc_restore_mode_datatype() + web::json::value make_restore_mode_datatype() { using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore mode is Modify"), U("Modify"), 0)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore mode is Rebuild"), U("Rebuild"), 1)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Restore mode is Modify"), U("Modify"), 0)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Restore mode is Rebuild"), U("Rebuild"), 1)); - return details::make_nc_datatype_descriptor_enum(U("Restore mode enumeration"), U("NcRestoreMode"), items, value::null()); + return details::make_datatype_descriptor_enum(U("Restore mode enumeration"), U("NcRestoreMode"), items, value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyholder - web::json::value make_nc_property_holder_datatype() + web::json::value make_property_holder_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property descriptor"), nmos::fields::nc::descriptor, U("NcPropertyDescriptor"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Property descriptor"), nmos::fields::nc::descriptor, U("NcPropertyDescriptor"), true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Property holder descriptor"), U("NcPropertyHolder"), fields, value::null()); + return details::make_datatype_descriptor_struct(U("Property holder descriptor"), U("NcPropertyHolder"), fields, value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiesholder - web::json::value make_nc_object_properties_holder_datatype() + web::json::value make_object_properties_holder_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Object role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Sequence of role paths which are a dependency for this object"), nmos::fields::nc::dependency_paths, U("NcRolePath"), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Sequence of class ids allowed as members of the block"), nmos::fields::nc::allowed_members_classes, U("NcClassId"), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Object properties"), nmos::fields::nc::values, U("NcPropertyHolder"), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Describes if the object is rebuildable"), nmos::fields::nc::is_rebuildable, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Object role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Sequence of role paths which are a dependency for this object"), nmos::fields::nc::dependency_paths, U("NcRolePath"), false, true, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Sequence of class ids allowed as members of the block"), nmos::fields::nc::allowed_members_classes, U("NcClassId"), false, true, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Object properties"), nmos::fields::nc::values, U("NcPropertyHolder"), false, true, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Describes if the object is rebuildable"), nmos::fields::nc::is_rebuildable, U("NcBoolean"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Object properties holder descriptor"), U("NcObjectPropertiesHolder"), fields, value::null()); + return details::make_datatype_descriptor_struct(U("Object properties holder descriptor"), U("NcObjectPropertiesHolder"), fields, value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesholder - web::json::value make_nc_bulk_properties_holder_datatype() + web::json::value make_bulk_properties_holder_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Optional vendor specific fingerprinting mechanism used for validation purposes"), nmos::fields::nc::validation_fingerprint, U("NcString"), true, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Values by rolePath"), nmos::fields::nc::values, U("NcObjectPropertiesHolder"), false, true, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Optional vendor specific fingerprinting mechanism used for validation purposes"), nmos::fields::nc::validation_fingerprint, U("NcString"), true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Values by rolePath"), nmos::fields::nc::values, U("NcObjectPropertiesHolder"), false, true, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Bulk properties holder descriptor"), U("NcBulkPropertiesHolder"), fields, value::null()); + return details::make_datatype_descriptor_struct(U("Bulk properties holder descriptor"), U("NcBulkPropertiesHolder"), fields, value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestorevalidationstatus - web::json::value make_nc_restore_validation_status_datatype() + web::json::value make_restore_validation_status_datatype() { using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore was successful"), U("Ok"), 200)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed"), U("Failed"), 400)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed because the role path is not found in the device model or the device cannot create the role path from the data set"), U("NotFound"), 404)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Restore failed due to an internal device error preventing the restore from happening"), U("DeviceError"), 500)); - return details::make_nc_datatype_descriptor_enum(U("Restore validation status enumeration"), U("NcRestoreValidationStatus"), items, value::null()); + web::json::push_back(items, details::make_enum_item_descriptor(U("Restore was successful"), U("Ok"), 200)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Restore failed"), U("Failed"), 400)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Restore failed because the role path is not found in the device model or the device cannot create the role path from the data set"), U("NotFound"), 404)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Restore failed due to an internal device error preventing the restore from happening"), U("DeviceError"), 500)); + return details::make_datatype_descriptor_enum(U("Restore validation status enumeration"), U("NcRestoreValidationStatus"), items, value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenoticetype - web::json::value make_nc_property_restore_notice_type_datatype() + web::json::value make_property_restore_notice_type_datatype() { using web::json::value; auto items = value::array(); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Warning property restore notice"), U("Warning"), nc_property_restore_notice_type::warning)); - web::json::push_back(items, details::make_nc_enum_item_descriptor(U("Error property restore notice"), U("Error"), nc_property_restore_notice_type::error)); - return details::make_nc_datatype_descriptor_enum(U("Property restore notice type enumeration"), U("NcPropertyRestoreNoticeType"), items, value::null()); + web::json::push_back(items, details::make_enum_item_descriptor(U("Warning property restore notice"), U("Warning"), nc_property_restore_notice_type::warning)); + web::json::push_back(items, details::make_enum_item_descriptor(U("Error property restore notice"), U("Error"), nc_property_restore_notice_type::error)); + return details::make_datatype_descriptor_enum(U("Property restore notice type enumeration"), U("NcPropertyRestoreNoticeType"), items, value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenotice - web::json::value make_nc_property_restore_notice_datatype() + web::json::value make_property_restore_notice_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property name"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property restore notice type"), nmos::fields::nc::notice_type, U("NcPropertyRestoreNoticeType"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Property restore notice message"), nmos::fields::nc::notice_message, U("NcString"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Property name"), nmos::fields::nc::name, U("NcName"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Property restore notice type"), nmos::fields::nc::notice_type, U("NcPropertyRestoreNoticeType"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Property restore notice message"), nmos::fields::nc::notice_message, U("NcString"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Property restore notice descriptor"), U("NcPropertyRestoreNotice"), fields, value::null()); + return details::make_datatype_descriptor_struct(U("Property restore notice descriptor"), U("NcPropertyRestoreNotice"), fields, value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiessetvalidation - web::json::value make_nc_object_properties_set_validation_datatype() + web::json::value make_object_properties_set_validation_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Object role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation status"), nmos::fields::nc::status, U("NcRestoreValidationStatus"), false, false, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation property notices"), nmos::fields::nc::notices, U("NcPropertyRestoreNotice"), false, true, value::null())); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Validation status message"), nmos::fields::nc::status_message, U("NcString"), true, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Object role path"), nmos::fields::nc::path, U("NcRolePath"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Validation status"), nmos::fields::nc::status, U("NcRestoreValidationStatus"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Validation property notices"), nmos::fields::nc::notices, U("NcPropertyRestoreNotice"), false, true, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Validation status message"), nmos::fields::nc::status_message, U("NcString"), true, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Object properties set validation descriptor"), U("NcObjectPropertiesSetValidation"), fields, value::null()); + return details::make_datatype_descriptor_struct(U("Object properties set validation descriptor"), U("NcObjectPropertiesSetValidation"), fields, value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncmethodresultbulkpropertiesholder - web::json::value make_nc_method_result_bulk_properties_holder_datatype() + web::json::value make_method_result_bulk_properties_holder_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Bulk properties holder value"), nmos::fields::nc::value, U("NcBulkPropertiesHolder"), false, false, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Bulk properties holder value"), nmos::fields::nc::value, U("NcBulkPropertiesHolder"), false, false, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Method result containing bulk properties holder descriptor"), U("NcMethodResultBulkPropertiesHolder"), fields, U("NcMethodResult"), value::null()); + return details::make_datatype_descriptor_struct(U("Method result containing bulk properties holder descriptor"), U("NcMethodResultBulkPropertiesHolder"), fields, U("NcMethodResult"), value::null()); } // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncmethodresultobjectpropertiessetvalidation - web::json::value make_nc_method_result_object_properties_set_validation_datatype() + web::json::value make_method_result_object_properties_set_validation_datatype() { using web::json::value; auto fields = value::array(); - web::json::push_back(fields, details::make_nc_field_descriptor(U("Object properties set path validation"), nmos::fields::nc::value, U("NcObjectPropertiesSetValidation"), false, true, value::null())); + web::json::push_back(fields, details::make_field_descriptor(U("Object properties set path validation"), nmos::fields::nc::value, U("NcObjectPropertiesSetValidation"), false, true, value::null())); - return details::make_nc_datatype_descriptor_struct(U("Method result containing object properties set validation descriptor"), U("NcMethodResultObjectPropertiesSetValidation"), fields, U("NcMethodResult"), value::null()); + return details::make_datatype_descriptor_struct(U("Method result containing object properties set validation descriptor"), U("NcMethodResultObjectPropertiesSetValidation"), fields, U("NcMethodResult"), value::null()); } } } diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index cb096ec45..eebf4fd75 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -47,144 +47,144 @@ namespace nmos namespace details { // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethodresult - web::json::value make_nc_method_result(const nc_method_result& method_result); - web::json::value make_nc_method_result_error(const nc_method_result& method_result, const utility::string_t& error_message); - web::json::value make_nc_method_result(const nc_method_result& method_result, const web::json::value& value); + web::json::value make_method_result(const nc_method_result& method_result); + web::json::value make_method_result_error(const nc_method_result& method_result, const utility::string_t& error_message); + web::json::value make_method_result(const nc_method_result& method_result, const web::json::value& value); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncelementid - web::json::value make_nc_element_id(const nc_element_id& element_id); - nc_element_id parse_nc_element_id(const web::json::value& element_id); + web::json::value make_element_id(const nc_element_id& element_id); + nc_element_id parse_element_id(const web::json::value& element_id); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventid - web::json::value make_nc_event_id(const nc_event_id& event_id); - nc_event_id parse_nc_event_id(const web::json::value& event_id); + web::json::value make_event_id(const nc_event_id& event_id); + nc_event_id parse_event_id(const web::json::value& event_id); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethodid - web::json::value make_nc_method_id(const nc_method_id& method_id); - nc_method_id parse_nc_method_id(const web::json::value& method_id); + web::json::value make_method_id(const nc_method_id& method_id); + nc_method_id parse_method_id(const web::json::value& method_id); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyid - web::json::value make_nc_property_id(const nc_property_id& property_id); - nc_property_id parse_nc_property_id(const web::json::value& property_id); + web::json::value make_property_id(const nc_property_id& property_id); + nc_property_id parse_property_id(const web::json::value& property_id); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassid - web::json::value make_nc_class_id(const nc_class_id& class_id); - nc_class_id parse_nc_class_id(const web::json::array& class_id); + web::json::value make_class_id(const nc_class_id& class_id); + nc_class_id parse_class_id(const web::json::array& class_id); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanufacturer - web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id, const web::uri& website); - web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id); - web::json::value make_nc_manufacturer(const utility::string_t& name); + web::json::value make_manufacturer(const utility::string_t& name, nc_organization_id organization_id, const web::uri& website); + web::json::value make_manufacturer(const utility::string_t& name, nc_organization_id organization_id); + web::json::value make_manufacturer(const utility::string_t& name); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncproduct - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + web::json::value make_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, const utility::string_t& brand_name, const nc_uuid& uuid, const utility::string_t& description); - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + web::json::value make_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, const utility::string_t& brand_name, const nc_uuid& uuid); - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + web::json::value make_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, const utility::string_t& brand_name); - web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level); + web::json::value make_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdeviceoperationalstate // device_specific_details can be null - web::json::value make_nc_device_operational_state(nc_device_generic_state::state generic_state, const web::json::value& device_specific_details); + web::json::value make_device_operational_state(nc_device_generic_state::state generic_state, const web::json::value& device_specific_details); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblockmemberdescriptor - web::json::value make_nc_block_member_descriptor(const utility::string_t& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const utility::string_t& user_label, nc_oid owner); + web::json::value make_block_member_descriptor(const utility::string_t& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const utility::string_t& user_label, nc_oid owner); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassdescriptor - web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); - web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); + web::json::value make_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); + web::json::value make_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& properties, const web::json::value& methods, const web::json::value& events); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncenumitemdescriptor - web::json::value make_nc_enum_item_descriptor(const utility::string_t& description, const nc_name& name, uint16_t val); + web::json::value make_enum_item_descriptor(const utility::string_t& description, const nc_name& name, uint16_t val); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nceventdescriptor - web::json::value make_nc_event_descriptor(const utility::string_t& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated); + web::json::value make_event_descriptor(const utility::string_t& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncfielddescriptor // constraints can be null - web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); - web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints); + web::json::value make_field_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); + web::json::value make_field_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmethoddescriptor // sequence parameters - web::json::value make_nc_method_descriptor(const utility::string_t& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated); + web::json::value make_method_descriptor(const utility::string_t& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterdescriptor // constraints can be null - web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints); - web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); + web::json::value make_parameter_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints); + web::json::value make_parameter_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertydescriptor // constraints can be null - web::json::value make_nc_property_descriptor(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, + web::json::value make_property_descriptor(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorenum // constraints can be null // items: sequence - web::json::value make_nc_datatype_descriptor_enum(const utility::string_t& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints); + web::json::value make_datatype_descriptor_enum(const utility::string_t& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorprimitive // constraints can be null - web::json::value make_nc_datatype_descriptor_primitive(const utility::string_t& description, const nc_name& name, const web::json::value& constraints); + web::json::value make_datatype_descriptor_primitive(const utility::string_t& description, const nc_name& name, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptorstruct // constraints can be null // fields: sequence - web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const utility::string_t& parent_type, const web::json::value& constraints); - web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const web::json::value& constraints); + web::json::value make_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const utility::string_t& parent_type, const web::json::value& constraints); + web::json::value make_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdatatypedescriptortypedef - web::json::value make_nc_datatype_typedef(const utility::string_t& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints); + web::json::value make_datatype_typedef(const utility::string_t& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraints - web::json::value make_nc_property_constraints(const nc_property_id& property_id, const web::json::value& default_value); + web::json::value make_property_constraints(const nc_property_id& property_id, const web::json::value& default_value); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsnumber - web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step); - web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t minimum, uint64_t maximum, uint64_t step); + web::json::value make_property_constraints_number(const nc_property_id& property_id, uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step); + web::json::value make_property_constraints_number(const nc_property_id& property_id, uint64_t minimum, uint64_t maximum, uint64_t step); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncpropertyconstraintsstring - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern); - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters, const nc_regex& pattern); - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters); - web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const nc_regex& pattern); + web::json::value make_property_constraints_string(const nc_property_id& property_id, const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern); + web::json::value make_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters, const nc_regex& pattern); + web::json::value make_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters); + web::json::value make_property_constraints_string(const nc_property_id& property_id, const nc_regex& pattern); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraints - web::json::value make_nc_parameter_constraints(const web::json::value& default_value); + web::json::value make_parameter_constraints(const web::json::value& default_value); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsnumber - web::json::value make_nc_parameter_constraints_number(uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step); - web::json::value make_nc_parameter_constraints_number(uint64_t minimum, uint64_t maximum, uint64_t step); + web::json::value make_parameter_constraints_number(uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step); + web::json::value make_parameter_constraints_number(uint64_t minimum, uint64_t maximum, uint64_t step); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncparameterconstraintsstring - web::json::value make_nc_parameter_constraints_string(const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern); - web::json::value make_nc_parameter_constraints_string(uint32_t max_characters, const nc_regex& pattern); - web::json::value make_nc_parameter_constraints_string(uint32_t max_characters); - web::json::value make_nc_parameter_constraints_string(const nc_regex& pattern); + web::json::value make_parameter_constraints_string(const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern); + web::json::value make_parameter_constraints_string(uint32_t max_characters, const nc_regex& pattern); + web::json::value make_parameter_constraints_string(uint32_t max_characters); + web::json::value make_parameter_constraints_string(const nc_regex& pattern); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpoint - web::json::value make_nc_touchpoint(const utility::string_t& context_namespace); + web::json::value make_touchpoint(const utility::string_t& context_namespace); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointnmos - web::json::value make_nc_touchpoint_nmos(const nc_touchpoint_resource_nmos& resource); + web::json::value make_touchpoint_nmos(const nc_touchpoint_resource_nmos& resource); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#nctouchpointnmoschannelmapping - web::json::value make_nc_touchpoint_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource); + web::json::value make_touchpoint_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject - web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); + web::json::value make_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock - web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members); + web::json::value make_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker - web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled); + web::json::value make_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstatusmonitor - web::json::value make_nc_status_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, uint64_t status_reporting_delay); + web::json::value make_status_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, uint64_t status_reporting_delay); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor web::json::value make_receiver_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, nc_link_status::status link_status, const utility::string_t& link_status_message, nc_connection_status::status connection_status, const utility::string_t& connection_status_message, nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, const web::json::value& synchronization_source_id, nc_stream_status::status stream_status, const utility::string_t& stream_status_message, uint32_t status_reporting_delay, bool auto_reset_monitor); @@ -193,33 +193,33 @@ namespace nmos web::json::value make_sender_monitor(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, nc_overall_status::status overall_status, const utility::string_t& overall_status_message, nc_link_status::status link_status, const utility::string_t& link_status_message, nc_transmission_status::status transmission_status, const utility::string_t& transmission_status_message, nc_synchronization_status::status external_synchronization_status, const utility::string_t& external_synchronization_status_message, const web::json::value& synchronization_source_id, nc_essence_status::status essence_status, const utility::string_t& essence_status_message, uint32_t status_reporting_delay, bool auto_reset_monitor); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager - web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); + web::json::value make_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager - web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, + web::json::value make_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state); + web::json::value make_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager - web::json::value make_nc_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); + web::json::value make_bulk_properties_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesholder - web::json::value make_nc_bulk_properties_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders); + web::json::value make_bulk_properties_holder(const utility::string_t& validation_fingerprint, const web::json::value& object_properties_holders); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyholder - web::json::value make_nc_property_holder(const nc_property_id& property_id, const web::json::value& descriptor, const web::json::value& property_value); + web::json::value make_property_holder(const nc_property_id& property_id, const web::json::value& descriptor, const web::json::value& property_value); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiesholder - web::json::value make_nc_object_properties_holder(const web::json::array& role_path, const web::json::array& property_holders, const web::json::array& dependency_paths, const web::json::array& allowed_members_classes, bool is_rebuildable); + web::json::value make_object_properties_holder(const web::json::array& role_path, const web::json::array& property_holders, const web::json::array& dependency_paths, const web::json::array& allowed_members_classes, bool is_rebuildable); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenotice - web::json::value make_nc_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message); + web::json::value make_property_restore_notice(const nc_property_id& property_id, const nc_name& name, nc_property_restore_notice_type::type notice_type, const utility::string_t& notice_message); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiessetvalidation - web::json::value make_nc_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const web::json::value& status_message); + web::json::value make_object_properties_set_validation(const web::json::array& role_path, nc_restore_validation_status::status status, const web::json::array& notices, const web::json::value& status_message); } // command message response @@ -250,258 +250,258 @@ namespace nmos // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/#control-class-models-for-branch-v10-dev // // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.html - web::json::value make_nc_object_class(); + web::json::value make_object_class(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.1.html - web::json::value make_nc_block_class(); + web::json::value make_block_class(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.2.html - web::json::value make_nc_worker_class(); + web::json::value make_worker_class(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.html - web::json::value make_nc_manager_class(); + web::json::value make_manager_class(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.1.html - web::json::value make_nc_device_manager_class(); + web::json::value make_device_manager_class(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/1.3.2.html - web::json::value make_nc_class_manager_class(); + web::json::value make_class_manager_class(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon - web::json::value make_nc_ident_beacon_class(); + web::json::value make_ident_beacon_class(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor - web::json::value make_nc_receiver_monitor_class(); + web::json::value make_receiver_monitor_class(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsendermonitor - web::json::value make_nc_sender_monitor_class(); + web::json::value make_sender_monitor_class(); // control classes properties/methods/events // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncobject - web::json::value make_nc_object_properties(); - web::json::value make_nc_object_methods(); - web::json::value make_nc_object_events(); + web::json::value make_object_properties(); + web::json::value make_object_methods(); + web::json::value make_object_events(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncblock - web::json::value make_nc_block_properties(); - web::json::value make_nc_block_methods(); - web::json::value make_nc_block_events(); + web::json::value make_block_properties(); + web::json::value make_block_methods(); + web::json::value make_block_events(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncworker - web::json::value make_nc_worker_properties(); - web::json::value make_nc_worker_methods(); - web::json::value make_nc_worker_events(); + web::json::value make_worker_properties(); + web::json::value make_worker_methods(); + web::json::value make_worker_events(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncmanager - web::json::value make_nc_manager_properties(); - web::json::value make_nc_manager_methods(); - web::json::value make_nc_manager_events(); + web::json::value make_manager_properties(); + web::json::value make_manager_methods(); + web::json::value make_manager_events(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncdevicemanager - web::json::value make_nc_device_manager_properties(); - web::json::value make_nc_device_manager_methods(); - web::json::value make_nc_device_manager_events(); + web::json::value make_device_manager_properties(); + web::json::value make_device_manager_methods(); + web::json::value make_device_manager_events(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#ncclassmanager - web::json::value make_nc_class_manager_properties(); - web::json::value make_nc_class_manager_methods(); - web::json::value make_nc_class_manager_events(); + web::json::value make_class_manager_properties(); + web::json::value make_class_manager_methods(); + web::json::value make_class_manager_events(); // Monitoring feature set control classes // https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstatusmonitor - web::json::value make_nc_status_monitor_properties(); - web::json::value make_nc_status_monitor_methods(); - web::json::value make_nc_status_monitor_events(); + web::json::value make_status_monitor_properties(); + web::json::value make_status_monitor_methods(); + web::json::value make_status_monitor_events(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncreceivermonitor - web::json::value make_nc_receiver_monitor_properties(); - web::json::value make_nc_receiver_monitor_methods(); - web::json::value make_nc_receiver_monitor_events(); + web::json::value make_receiver_monitor_properties(); + web::json::value make_receiver_monitor_methods(); + web::json::value make_receiver_monitor_events(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsendermonitor - web::json::value make_nc_sender_monitor_properties(); - web::json::value make_nc_sender_monitor_methods(); - web::json::value make_nc_sender_monitor_events(); + web::json::value make_sender_monitor_properties(); + web::json::value make_sender_monitor_methods(); + web::json::value make_sender_monitor_events(); // Identification feature set control classes // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#ncidentbeacon - web::json::value make_nc_ident_beacon_properties(); - web::json::value make_nc_ident_beacon_methods(); - web::json::value make_nc_ident_beacon_events(); + web::json::value make_ident_beacon_properties(); + web::json::value make_ident_beacon_methods(); + web::json::value make_ident_beacon_events(); // Device configuration classes // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesmanager - web::json::value make_nc_bulk_properties_manager_properties(); - web::json::value make_nc_bulk_properties_manager_methods(); - web::json::value make_nc_bulk_properties_manager_events(); + web::json::value make_bulk_properties_manager_properties(); + web::json::value make_bulk_properties_manager_methods(); + web::json::value make_bulk_properties_manager_events(); // Datatype models // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/#datatype-models-for-branch-v10-dev // // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_boolean_datatype(); + web::json::value make_boolean_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_int16_datatype(); + web::json::value make_int16_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_int32_datatype(); + web::json::value make_int32_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_int64_datatype(); + web::json::value make_int64_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_uint16_datatype(); + web::json::value make_uint16_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_uint32_datatype(); + web::json::value make_uint32_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_uint64_datatype(); + web::json::value make_uint64_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_float32_datatype(); + web::json::value make_float32_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_float64_datatype(); + web::json::value make_float64_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/docs/Framework.html#primitives - web::json::value make_nc_string_datatype(); + web::json::value make_string_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcBlockMemberDescriptor.html - web::json::value make_nc_block_member_descriptor_datatype(); + web::json::value make_block_member_descriptor_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassDescriptor.html - web::json::value make_nc_class_descriptor_datatype(); + web::json::value make_class_descriptor_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcClassId.html - web::json::value make_nc_class_id_datatype(); + web::json::value make_class_id_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptor.html - web::json::value make_nc_datatype_descriptor_datatype(); + web::json::value make_datatype_descriptor_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorEnum.html - web::json::value make_nc_datatype_descriptor_enum_datatype(); + web::json::value make_datatype_descriptor_enum_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorPrimitive.html - web::json::value make_nc_datatype_descriptor_primitive_datatype(); + web::json::value make_datatype_descriptor_primitive_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorStruct.html - web::json::value make_nc_datatype_descriptor_struct_datatype(); + web::json::value make_datatype_descriptor_struct_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeDescriptorTypeDef.html - web::json::value make_nc_datatype_descriptor_type_def_datatype(); + web::json::value make_datatype_descriptor_type_def_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDatatypeType.html - web::json::value make_nc_datatype_type_datatype(); + web::json::value make_datatype_type_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDescriptor.html - web::json::value make_nc_descriptor_datatype(); + web::json::value make_descriptor_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceGenericState.html - web::json::value make_nc_device_generic_state_datatype(); + web::json::value make_device_generic_state_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcDeviceOperationalState.html - web::json::value make_nc_device_operational_state_datatype(); + web::json::value make_device_operational_state_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcElementId.html - web::json::value make_nc_element_id_datatype(); + web::json::value make_element_id_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEnumItemDescriptor.html - web::json::value make_nc_enum_item_descriptor_datatype(); + web::json::value make_enum_item_descriptor_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventDescriptor.html - web::json::value make_nc_event_descriptor_datatype(); + web::json::value make_event_descriptor_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcEventId.html - web::json::value make_nc_event_id_datatype(); + web::json::value make_event_id_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcFieldDescriptor.html - web::json::value make_nc_field_descriptor_datatype(); + web::json::value make_field_descriptor_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcId.html - web::json::value make_nc_id_datatype(); + web::json::value make_id_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcManufacturer.html - web::json::value make_nc_manufacturer_datatype(); + web::json::value make_manufacturer_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodDescriptor.html - web::json::value make_nc_method_descriptor_datatype(); + web::json::value make_method_descriptor_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodId.html - web::json::value make_nc_method_id_datatype(); + web::json::value make_method_id_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResult.html - web::json::value make_nc_method_result_datatype(); + web::json::value make_method_result_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultBlockMemberDescriptors.html - web::json::value make_nc_method_result_block_member_descriptors_datatype(); + web::json::value make_method_result_block_member_descriptors_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultClassDescriptor.html - web::json::value make_nc_method_result_class_descriptor_datatype(); + web::json::value make_method_result_class_descriptor_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultDatatypeDescriptor.html - web::json::value make_nc_method_result_datatype_descriptor_datatype(); + web::json::value make_method_result_datatype_descriptor_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultError.html - web::json::value make_nc_method_result_error_datatype(); + web::json::value make_method_result_error_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultId.html - web::json::value make_nc_method_result_id_datatype(); + web::json::value make_method_result_id_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultLength.html - web::json::value make_nc_method_result_length_datatype(); + web::json::value make_method_result_length_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodResultPropertyValue.html - web::json::value make_nc_method_result_property_value_datatype(); + web::json::value make_method_result_property_value_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcMethodStatus.html - web::json::value make_nc_method_status_datatype(); + web::json::value make_method_status_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcName.html - web::json::value make_nc_name_datatype(); + web::json::value make_name_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOid.html - web::json::value make_nc_oid_datatype(); + web::json::value make_oid_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcOrganizationId.html - web::json::value make_nc_organization_id_datatype(); + web::json::value make_organization_id_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraints.html - web::json::value make_nc_parameter_constraints_datatype(); + web::json::value make_parameter_constraints_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsNumber.html - web::json::value make_nc_parameter_constraints_number_datatype(); + web::json::value make_parameter_constraints_number_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterConstraintsString.html - web::json::value make_nc_parameter_constraints_string_datatype(); + web::json::value make_parameter_constraints_string_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcParameterDescriptor.html - web::json::value make_nc_parameter_descriptor_datatype(); + web::json::value make_parameter_descriptor_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcProduct.html - web::json::value make_nc_product_datatype(); + web::json::value make_product_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangeType.html - web::json::value make_nc_property_change_type_datatype(); + web::json::value make_property_change_type_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyChangedEventData.html - web::json::value make_nc_property_changed_event_data_datatype(); + web::json::value make_property_changed_event_data_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraints.html - web::json::value make_nc_property_contraints_datatype(); + web::json::value make_property_contraints_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsNumber.html - web::json::value make_nc_property_constraints_number_datatype(); + web::json::value make_property_constraints_number_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyConstraintsString.html - web::json::value make_nc_property_constraints_string_datatype(); + web::json::value make_property_constraints_string_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyDescriptor.html - web::json::value make_nc_property_descriptor_datatype(); + web::json::value make_property_descriptor_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcPropertyId.html - web::json::value make_nc_property_id_datatype(); + web::json::value make_property_id_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRegex.html - web::json::value make_nc_regex_datatype(); + web::json::value make_regex_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcResetCause.html - web::json::value make_nc_reset_cause_datatype(); + web::json::value make_reset_cause_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcRolePath.html - web::json::value make_nc_role_path_datatype(); + web::json::value make_role_path_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTimeInterval.html - web::json::value make_nc_time_interval_datatype(); + web::json::value make_time_interval_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpoint.html - web::json::value make_nc_touchpoint_datatype(); + web::json::value make_touchpoint_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmos.html - web::json::value make_nc_touchpoint_nmos_datatype(); + web::json::value make_touchpoint_nmos_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointNmosChannelMapping.html - web::json::value make_nc_touchpoint_nmos_channel_mapping_datatype(); + web::json::value make_touchpoint_nmos_channel_mapping_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResource.html - web::json::value make_nc_touchpoint_resource_datatype(); + web::json::value make_touchpoint_resource_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmos.html - web::json::value make_nc_touchpoint_resource_nmos_datatype(); + web::json::value make_touchpoint_resource_nmos_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcTouchpointResourceNmosChannelMapping.html - web::json::value make_nc_touchpoint_resource_nmos_channel_mapping_datatype(); + web::json::value make_touchpoint_resource_nmos_channel_mapping_datatype(); // See // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUri.html - web::json::value make_nc_uri_datatype(); + web::json::value make_uri_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcUuid.html - web::json::value make_nc_uuid_datatype(); + web::json::value make_uuid_datatype(); // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/NcVersionCode.html - web::json::value make_nc_version_code_datatype(); + web::json::value make_version_code_datatype(); // Monitoring feature set datatypes // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#datatypes // // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncconnectionstatus - web::json::value make_nc_connection_status_datatype(); + web::json::value make_connection_status_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncessencestatus - web::json::value make_nc_essence_status_datatype(); + web::json::value make_essence_status_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncoverallstatus - web::json::value make_nc_overall_status_datatype(); + web::json::value make_overall_status_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nclinkstatus - web::json::value make_nc_link_status_datatype(); + web::json::value make_link_status_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncsynchronizationstatus - web::json::value make_nc_synchronization_status_datatype(); + web::json::value make_synchronization_status_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncstreamstatus - web::json::value make_nc_stream_status_datatype(); + web::json::value make_stream_status_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nccounter - web::json::value make_nc_counter_datatype(); + web::json::value make_counter_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#nctransmissionstatus - web::json::value make_nc_transmission_status_datatype(); + web::json::value make_transmission_status_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#ncmethodresultcounters - web::json::value make_nc_method_result_counters_datatype(); + web::json::value make_method_result_counters_datatype(); // Device configuration feature set datatypes // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestoremode - web::json::value make_nc_restore_mode_datatype(); + web::json::value make_restore_mode_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyholder - web::json::value make_nc_property_holder_datatype(); + web::json::value make_property_holder_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiesholder - web::json::value make_nc_object_properties_holder_datatype(); + web::json::value make_object_properties_holder_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncbulkpropertiesholder - web::json::value make_nc_bulk_properties_holder_datatype(); + web::json::value make_bulk_properties_holder_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncrestorevalidationstatus - web::json::value make_nc_restore_validation_status_datatype(); + web::json::value make_restore_validation_status_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenoticetype - web::json::value make_nc_property_restore_notice_type_datatype(); + web::json::value make_property_restore_notice_type_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncpropertyrestorenotice - web::json::value make_nc_property_restore_notice_datatype(); + web::json::value make_property_restore_notice_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncobjectpropertiessetvalidation - web::json::value make_nc_object_properties_set_validation_datatype(); + web::json::value make_object_properties_set_validation_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncmethodresultbulkpropertiesholder - web::json::value make_nc_method_result_bulk_properties_holder_datatype(); + web::json::value make_method_result_bulk_properties_holder_datatype(); // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncmethodresultobjectpropertiessetvalidation - web::json::value make_nc_method_result_object_properties_set_validation_datatype(); + web::json::value make_method_result_object_properties_set_validation_datatype(); } } #endif diff --git a/Development/nmos/control_protocol_resources.cpp b/Development/nmos/control_protocol_resources.cpp index 5d143ea05..9ba45c47e 100644 --- a/Development/nmos/control_protocol_resources.cpp +++ b/Development/nmos/control_protocol_resources.cpp @@ -13,7 +13,7 @@ namespace nmos { using web::json::value; - auto data = nc::details::make_nc_block(nc_block_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true, members); + auto data = nc::details::make_block(nc_block_class_id, oid, true, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, true, members); return{ is12_versions::v1_0, types::nc_block, std::move(data), true }; } @@ -44,7 +44,7 @@ namespace nmos for(const auto& class_id: allowed_member_classes) { - web::json::push_back(allowed_member_classes_array, nc::details::make_nc_class_id(class_id)); + web::json::push_back(allowed_member_classes_array, nc::details::make_class_id(class_id)); } control_protocol_resource.data[nmos::fields::nc::allowed_members_classes] = allowed_member_classes_array; @@ -83,14 +83,14 @@ namespace nmos { using web::json::value; - const auto& manufacturer = nc::details::make_nc_manufacturer(nmos::experimental::fields::manufacturer_name(settings)); - const auto& product = nc::details::make_nc_product(nmos::experimental::fields::product_name(settings), nmos::experimental::fields::product_key(settings), nmos::experimental::fields::product_key(settings)); + const auto& manufacturer = nc::details::make_manufacturer(nmos::experimental::fields::manufacturer_name(settings)); + const auto& product = nc::details::make_product(nmos::experimental::fields::product_name(settings), nmos::experimental::fields::product_key(settings), nmos::experimental::fields::product_key(settings)); const auto& serial_number = nmos::experimental::fields::serial_number(settings); const auto device_name = value::null(); const auto device_role = value::null(); - const auto& operational_state = nc::details::make_nc_device_operational_state(nc_device_generic_state::normal_operation, value::null()); + const auto& operational_state = nc::details::make_device_operational_state(nc_device_generic_state::normal_operation, value::null()); - auto data = nc::details::make_nc_device_manager(oid, root_block_oid, value::string(U("Device manager")), U("The device manager offers information about the product this device is representing"), value::null(), value::null(), + auto data = nc::details::make_device_manager(oid, root_block_oid, value::string(U("Device manager")), U("The device manager offers information about the product this device is representing"), value::null(), value::null(), manufacturer, product, serial_number, value::null(), device_name, device_role, operational_state, nc_reset_cause::unknown); return{ is12_versions::v1_0, types::nc_device_manager, std::move(data), true }; @@ -101,7 +101,7 @@ namespace nmos { using web::json::value; - auto data = nc::details::make_nc_class_manager(oid, root_block_oid, value::string(U("Class manager")), U("The class manager offers access to control class and data type descriptors"), value::null(), value::null(), control_protocol_state); + auto data = nc::details::make_class_manager(oid, root_block_oid, value::string(U("Class manager")), U("The class manager offers access to control class and data type descriptors"), value::null(), value::null(), control_protocol_state); return{ is12_versions::v1_0, types::nc_class_manager, std::move(data), true }; } @@ -134,7 +134,7 @@ namespace nmos { using web::json::value; - auto data = nc::details::make_nc_worker(nc_ident_beacon_class_id, oid, constant_oid, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, enabled); + auto data = nc::details::make_worker(nc_ident_beacon_class_id, oid, constant_oid, owner, role, value::string(user_label), description, touchpoints, runtime_property_constraints, enabled); data[nmos::fields::nc::active] = value::boolean(active); return{ is12_versions::v1_0, types::nc_ident_beacon, std::move(data), true }; @@ -147,7 +147,7 @@ namespace nmos { using web::json::value; - auto data = nc::details::make_nc_bulk_properties_manager(oid, root_block_oid, value::string(U("Bulk properties manager")), U("The bulk properties manager offers a central model for getting and setting properties of multiple role paths"), value::null(), value::null()); + auto data = nc::details::make_bulk_properties_manager(oid, root_block_oid, value::string(U("Bulk properties manager")), U("The bulk properties manager offers a central model for getting and setting properties of multiple role paths"), value::null(), value::null()); return{ is12_versions::v1_0, types::nc_bulk_properties_manager, std::move(data), true }; } diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index 93ca5c20e..fe5ecca34 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -14,8 +14,8 @@ namespace nmos // create control class descriptor // where // properties: vector of NcPropertyDescriptor where NcPropertyDescriptor can be constructed using make_control_class_property - // methods: vector of NcMethodDescriptor vs assoicated method handler where NcMethodDescriptor can be constructed using make_nc_method_descriptor - // events: vector of NcEventDescriptor where NcEventDescriptor can be constructed using make_nc_event_descriptor + // methods: vector of NcMethodDescriptor vs assoicated method handler where NcMethodDescriptor can be constructed using make_method_descriptor + // events: vector of NcEventDescriptor where NcEventDescriptor can be constructed using make_event_descriptor control_class_descriptor make_control_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& fixed_role, const std::vector& properties_, const std::vector& methods_, const std::vector& events_) { using web::json::value; @@ -31,8 +31,8 @@ namespace nmos // create control class descriptor with fixed role // where // properties: vector of NcPropertyDescriptor where NcPropertyDescriptor can be constructed using make_control_class_property - // methods: vector of NcMethodDescriptor where NcMethodDescriptor can be constructed using make_nc_method_descriptor and the assoicated method handler - // events: vector of NcEventDescriptor where NcEventDescriptor can be constructed using make_nc_event_descriptor + // methods: vector of NcMethodDescriptor where NcMethodDescriptor can be constructed using make_method_descriptor and the assoicated method handler + // events: vector of NcEventDescriptor where NcEventDescriptor can be constructed using make_event_descriptor control_class_descriptor make_control_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const std::vector& properties, const std::vector& methods, const std::vector& events) { using web::json::value; @@ -42,8 +42,8 @@ namespace nmos // create control class descriptor without fixed role // where // properties: vector of NcPropertyDescriptor where NcPropertyDescriptor can be constructed using make_control_class_property - // methods: vector of NcMethodDescriptor where NcMethodDescriptor can be constructed using make_nc_method_descriptor and the assoicated method handler - // events: vector of NcEventDescriptor where NcEventDescriptor can be constructed using make_nc_event_descriptor + // methods: vector of NcMethodDescriptor where NcMethodDescriptor can be constructed using make_method_descriptor and the assoicated method handler + // events: vector of NcEventDescriptor where NcEventDescriptor can be constructed using make_event_descriptor control_class_descriptor make_control_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const std::vector& properties, const std::vector& methods, const std::vector& events) { using web::json::value; @@ -54,13 +54,13 @@ namespace nmos // create control class property descriptor web::json::value make_control_class_property_descriptor(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) { - return nc::details::make_nc_property_descriptor(description, id, name, type_name, is_read_only, is_nullable, is_sequence, is_deprecated, constraints); + return nc::details::make_property_descriptor(description, id, name, type_name, is_read_only, is_nullable, is_sequence, is_deprecated, constraints); } // create control class method parameter descriptor web::json::value make_control_class_method_parameter_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { - return nc::details::make_nc_parameter_descriptor(description, name, type_name, is_nullable, is_sequence, constraints); + return nc::details::make_parameter_descriptor(description, name, type_name, is_nullable, is_sequence, constraints); } namespace details @@ -72,7 +72,7 @@ namespace nmos value parameters = value::array(); for (const auto& parameter : parameters_) { web::json::push_back(parameters, parameter); } - return nc::details::make_nc_method_descriptor(description, id, name, result_datatype, parameters, is_deprecated); + return nc::details::make_method_descriptor(description, id, name, result_datatype, parameters, is_deprecated); } } // create control class method descriptor @@ -84,7 +84,7 @@ namespace nmos // create control class event descriptor web::json::value make_control_class_event_descriptor(const utility::string_t& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated) { - return nc::details::make_nc_event_descriptor(description, id, name, event_datatype, is_deprecated); + return nc::details::make_event_descriptor(description, id, name, event_datatype, is_deprecated); } namespace details @@ -200,10 +200,10 @@ namespace nmos if (data_set.is_null()) { - return nc::details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("Null dataSet parameter")); + return nc::details::make_method_result_error({ nc_method_status::parameter_error }, U("Null dataSet parameter")); } - auto result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); + auto result = nc::details::make_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("not implemented")); if (get_read_only_modification_allow_list && remove_device_model_object && create_device_model_object) { result = validate_set_properties_by_path(resources, resource, data_set, recurse, static_cast(restore_mode), get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); @@ -211,7 +211,7 @@ namespace nmos const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) { - return nc::details::make_nc_method_result({ nmos::nc_method_status::method_deprecated }, nmos::fields::nc::value(result)); + return nc::details::make_method_result({ nmos::nc_method_status::method_deprecated }, nmos::fields::nc::value(result)); } } return result; @@ -227,10 +227,10 @@ namespace nmos if (data_set.is_null()) { - return nc::details::make_nc_method_result_error({ nc_method_status::parameter_error }, U("Null dataSet parameter")); + return nc::details::make_method_result_error({ nc_method_status::parameter_error }, U("Null dataSet parameter")); } - auto result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("callbacks not implemented")); + auto result = nc::details::make_method_result_error({ nmos::nc_method_status::method_not_implemented }, U("callbacks not implemented")); if (get_read_only_modification_allow_list && remove_device_model_object && create_device_model_object) { result = set_properties_by_path(resources, resource, data_set, recurse, static_cast(restore_mode), get_control_protocol_class_descriptor, get_control_protocol_datatype_descriptor, validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object); @@ -238,7 +238,7 @@ namespace nmos const auto& status = nmos::fields::nc::status(result); if (!web::http::is_error_status_code((web::http::status_code)status) && is_deprecated) { - return nc::details::make_nc_method_result({ nmos::nc_method_status::method_deprecated }, nmos::fields::nc::value(result)); + return nc::details::make_method_result({ nmos::nc_method_status::method_deprecated }, nmos::fields::nc::value(result)); } } return result; @@ -290,7 +290,7 @@ namespace nmos { for (const auto& nc_method_descriptor : nc_method_descriptors.as_array()) { - methods.push_back(make_control_class_method(nc_method_descriptor, method_handlers.at(nc::details::parse_nc_method_id(nmos::fields::nc::id(nc_method_descriptor))))); + methods.push_back(make_control_class_method(nc_method_descriptor, method_handlers.at(nc::details::parse_method_id(nmos::fields::nc::id(nc_method_descriptor))))); } } return methods; @@ -308,9 +308,9 @@ namespace nmos // NcObject { nc_object_class_id, make_control_class_descriptor(U("NcObject class descriptor"), nc_object_class_id, U("NcObject"), // NcObject properties - to_vector(nc::make_nc_object_properties()), + to_vector(nc::make_object_properties()), // NcObject methods - to_methods_vector(nc::make_nc_object_methods(), + to_methods_vector(nc::make_object_methods(), { // link NcObject method_ids with method functions { nc_object_get_method_id, details::make_nc_get_handler(get_control_protocol_class_descriptor) }, @@ -322,13 +322,13 @@ namespace nmos { nc_object_get_sequence_length_method_id, details::make_nc_get_sequence_length_handler(get_control_protocol_class_descriptor) } }), // NcObject events - to_vector(nc::make_nc_object_events())) }, + to_vector(nc::make_object_events())) }, // NcBlock { nc_block_class_id, make_control_class_descriptor(U("NcBlock class descriptor"), nc_block_class_id, U("NcBlock"), // NcBlock properties - to_vector(nc::make_nc_block_properties()), + to_vector(nc::make_block_properties()), // NcBlock methods - to_methods_vector(nc::make_nc_block_methods(), + to_methods_vector(nc::make_block_methods(), { // link NcBlock method_ids with method functions { nc_block_get_member_descriptors_method_id, details::make_nc_get_member_descriptors_handler() }, @@ -337,70 +337,70 @@ namespace nmos { nc_block_find_members_by_class_id_method_id, details::make_nc_find_members_by_class_id_handler() } }), // NcBlock events - to_vector(nc::make_nc_block_events())) }, + to_vector(nc::make_block_events())) }, // NcWorker { nc_worker_class_id, make_control_class_descriptor(U("NcWorker class descriptor"), nc_worker_class_id, U("NcWorker"), // NcWorker properties - to_vector(nc::make_nc_worker_properties()), + to_vector(nc::make_worker_properties()), // NcWorker methods - to_methods_vector(nc::make_nc_worker_methods(), {}), + to_methods_vector(nc::make_worker_methods(), {}), // NcWorker events - to_vector(nc::make_nc_worker_events())) }, + to_vector(nc::make_worker_events())) }, // NcManager { nc_manager_class_id, make_control_class_descriptor(U("NcManager class descriptor"), nc_manager_class_id, U("NcManager"), // NcManager properties - to_vector(nc::make_nc_manager_properties()), + to_vector(nc::make_manager_properties()), // NcManager methods - to_methods_vector(nc::make_nc_manager_methods(), {}), + to_methods_vector(nc::make_manager_methods(), {}), // NcManager events - to_vector(nc::make_nc_manager_events())) }, + to_vector(nc::make_manager_events())) }, // NcDeviceManager { nc_device_manager_class_id, make_control_class_descriptor(U("NcDeviceManager class descriptor"), nc_device_manager_class_id, U("NcDeviceManager"), U("DeviceManager"), // NcDeviceManager properties - to_vector(nc::make_nc_device_manager_properties()), + to_vector(nc::make_device_manager_properties()), // NcDeviceManager methods - to_methods_vector(nc::make_nc_device_manager_methods(), {}), + to_methods_vector(nc::make_device_manager_methods(), {}), // NcDeviceManager events - to_vector(nc::make_nc_device_manager_events())) }, + to_vector(nc::make_device_manager_events())) }, // NcClassManager { nc_class_manager_class_id, make_control_class_descriptor(U("NcClassManager class descriptor"), nc_class_manager_class_id, U("NcClassManager"), U("ClassManager"), // NcClassManager properties - to_vector(nc::make_nc_class_manager_properties()), + to_vector(nc::make_class_manager_properties()), // NcClassManager methods - to_methods_vector(nc::make_nc_class_manager_methods(), + to_methods_vector(nc::make_class_manager_methods(), { // link NcClassManager method_ids with method functions { nc_class_manager_get_control_class_method_id, details::make_nc_get_control_class_handler(get_control_protocol_class_descriptor) }, { nc_class_manager_get_datatype_method_id, details::make_nc_get_datatype_handler(get_control_protocol_datatype_descriptor) } }), // NcClassManager events - to_vector(nc::make_nc_class_manager_events())) }, + to_vector(nc::make_class_manager_events())) }, // Identification feature set // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/identification/#control-classes // NcIdentBeacon { nc_ident_beacon_class_id, make_control_class_descriptor(U("NcIdentBeacon class descriptor"), nc_ident_beacon_class_id, U("NcIdentBeacon"), // NcIdentBeacon properties - to_vector(nc::make_nc_ident_beacon_properties()), + to_vector(nc::make_ident_beacon_properties()), // NcIdentBeacon methods - to_methods_vector(nc::make_nc_ident_beacon_methods(), {}), + to_methods_vector(nc::make_ident_beacon_methods(), {}), // NcIdentBeacon events - to_vector(nc::make_nc_ident_beacon_events())) }, + to_vector(nc::make_ident_beacon_events())) }, // Monitoring feature set // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#control-classes // NcStatusMonitor { nc_status_monitor_class_id, make_control_class_descriptor(U("NcStatusMonitor class descriptor"), nc_status_monitor_class_id, U("NcStatusMonitor"), // NcReceiverMonitor properties - to_vector(nc::make_nc_status_monitor_properties()), + to_vector(nc::make_status_monitor_properties()), // NcReceiverMonitor methods - to_methods_vector(nc::make_nc_status_monitor_methods(), {}), + to_methods_vector(nc::make_status_monitor_methods(), {}), // NcReceiverMonitor events - to_vector(nc::make_nc_status_monitor_events())) }, + to_vector(nc::make_status_monitor_events())) }, // NcReceiverMonitor { nc_receiver_monitor_class_id, make_control_class_descriptor(U("NcReceiverMonitor class descriptor"), nc_receiver_monitor_class_id, U("NcReceiverMonitor"), // NcReceiverMonitor properties - to_vector(nc::make_nc_receiver_monitor_properties()), + to_vector(nc::make_receiver_monitor_properties()), // NcReceiverMonitor methods - to_methods_vector(nc::make_nc_receiver_monitor_methods(), + to_methods_vector(nc::make_receiver_monitor_methods(), { // link NcReceiverMonitor method_ids with method functions { nc_receiver_monitor_get_lost_packet_counters_method_id, details::make_nc_get_lost_packet_counters_handler(get_lost_packet_counters)}, @@ -408,13 +408,13 @@ namespace nmos { nc_receiver_monitor_reset_monitor_method_id, details::make_nc_reset_monitor_handler(get_control_protocol_class_descriptor, property_changed, reset_monitor)} }), // NcReceiverMonitor events - to_vector(nc::make_nc_receiver_monitor_events())) }, + to_vector(nc::make_receiver_monitor_events())) }, // NcSenderMonitor { nc_sender_monitor_class_id, make_control_class_descriptor(U("NcSenderMonitor class descriptor"), nc_sender_monitor_class_id, U("NcSenderMonitor"), // NcSenderMonitor properties - to_vector(nc::make_nc_sender_monitor_properties()), + to_vector(nc::make_sender_monitor_properties()), // NcSenderMonitor methods - to_methods_vector(nc::make_nc_sender_monitor_methods(), + to_methods_vector(nc::make_sender_monitor_methods(), { // link NcSenderMonitor method_ids with method functions // TODO: implement actual GetTransmissionError and ResetCountersAndMessages function @@ -422,17 +422,17 @@ namespace nmos { nc_sender_monitor_reset_monitor_method_id, details::make_nc_reset_monitor_handler(get_control_protocol_class_descriptor, property_changed, reset_monitor)} }), // NcSenderMonitor events - to_vector(nc::make_nc_sender_monitor_events())) }, + to_vector(nc::make_sender_monitor_events())) }, // NcBulkPropertiesManager { nc_bulk_properties_manager_class_id, make_control_class_descriptor(U("NcBulkPropertiesManager class descriptor"), nc_bulk_properties_manager_class_id, U("NcBulkPropertiesManager"), U("BulkPropertiesManager"), - to_vector(nc::make_nc_bulk_properties_manager_properties()), - to_methods_vector(nc::make_nc_bulk_properties_manager_methods(), + to_vector(nc::make_bulk_properties_manager_properties()), + to_methods_vector(nc::make_bulk_properties_manager_methods(), { { nc_bulk_properties_manager_get_properties_by_path_method_id, details::make_nc_get_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), create_validation_fingerprint)}, { nc_bulk_properties_manager_validate_set_properties_by_path_method_id, details::make_nc_validate_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object) }, { nc_bulk_properties_manager_set_properties_by_path_method_id, details::make_nc_set_properties_by_path_handler(make_get_control_protocol_class_descriptor_handler(*this), make_get_control_protocol_datatype_descriptor_handler(*this), validate_validation_fingerprint, get_read_only_modification_allow_list, remove_device_model_object, create_device_model_object) } }), - to_vector(nc::make_nc_bulk_properties_manager_events())) } + to_vector(nc::make_bulk_properties_manager_events())) } }; // setup the standard datatypes @@ -440,97 +440,97 @@ namespace nmos { // Datatype models // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/datatypes/ - { U("NcBoolean"), {nc::make_nc_boolean_datatype()} }, - { U("NcInt16"), {nc::make_nc_int16_datatype()} }, - { U("NcInt32"), {nc::make_nc_int32_datatype()} }, - { U("NcInt64"), {nc::make_nc_int64_datatype()} }, - { U("NcUint16"), {nc::make_nc_uint16_datatype()} }, - { U("NcUint32"), {nc::make_nc_uint32_datatype()} }, - { U("NcUint64"), {nc::make_nc_uint64_datatype()} }, - { U("NcFloat32"), {nc::make_nc_float32_datatype()} }, - { U("NcFloat64"), {nc::make_nc_float64_datatype()} }, - { U("NcString"), {nc::make_nc_string_datatype()} }, - { U("NcClassId"), {nc::make_nc_class_id_datatype()} }, - { U("NcOid"), {nc::make_nc_oid_datatype()} }, - { U("NcTouchpoint"), {nc::make_nc_touchpoint_datatype()} }, - { U("NcElementId"), {nc::make_nc_element_id_datatype()} }, - { U("NcPropertyId"), {nc::make_nc_property_id_datatype()} }, - { U("NcPropertyConstraints"), {nc::make_nc_property_contraints_datatype()} }, - { U("NcMethodResultPropertyValue"), {nc::make_nc_method_result_property_value_datatype()} }, - { U("NcMethodStatus"), {nc::make_nc_method_status_datatype()} }, - { U("NcMethodResult"), {nc::make_nc_method_result_datatype()} }, - { U("NcId"), {nc::make_nc_id_datatype()} }, - { U("NcMethodResultId"), {nc::make_nc_method_result_id_datatype()} }, - { U("NcMethodResultLength"), {nc::make_nc_method_result_length_datatype()} }, - { U("NcPropertyChangeType"), {nc::make_nc_property_change_type_datatype()} }, - { U("NcPropertyChangedEventData"), {nc::make_nc_property_changed_event_data_datatype()} }, - { U("NcDescriptor"), {nc::make_nc_descriptor_datatype()} }, - { U("NcBlockMemberDescriptor"), {nc::make_nc_block_member_descriptor_datatype()} }, - { U("NcMethodResultBlockMemberDescriptors"), {nc::make_nc_method_result_block_member_descriptors_datatype()} }, - { U("NcVersionCode"), {nc::make_nc_version_code_datatype()} }, - { U("NcOrganizationId"), {nc::make_nc_organization_id_datatype()} }, - { U("NcUri"), {nc::make_nc_uri_datatype()} }, - { U("NcManufacturer"), {nc::make_nc_manufacturer_datatype()} }, - { U("NcUuid"), {nc::make_nc_uuid_datatype()} }, - { U("NcProduct"), {nc::make_nc_product_datatype()} }, - { U("NcDeviceGenericState"), {nc::make_nc_device_generic_state_datatype()} }, - { U("NcDeviceOperationalState"), {nc::make_nc_device_operational_state_datatype()} }, - { U("NcResetCause"), {nc::make_nc_reset_cause_datatype()} }, - { U("NcName"), {nc::make_nc_name_datatype()} }, - { U("NcPropertyDescriptor"), {nc::make_nc_property_descriptor_datatype()} }, - { U("NcParameterDescriptor"), {nc::make_nc_parameter_descriptor_datatype()} }, - { U("NcMethodId"), {nc::make_nc_method_id_datatype()} }, - { U("NcMethodDescriptor"), {nc::make_nc_method_descriptor_datatype()} }, - { U("NcEventId"), {nc::make_nc_event_id_datatype()} }, - { U("NcEventDescriptor"), {nc::make_nc_event_descriptor_datatype()} }, - { U("NcClassDescriptor"), {nc::make_nc_class_descriptor_datatype()} }, - { U("NcParameterConstraints"), {nc::make_nc_parameter_constraints_datatype()} }, - { U("NcDatatypeType"), {nc::make_nc_datatype_type_datatype()} }, - { U("NcDatatypeDescriptor"), {nc::make_nc_datatype_descriptor_datatype()} }, - { U("NcMethodResultClassDescriptor"), {nc::make_nc_method_result_class_descriptor_datatype()} }, - { U("NcMethodResultDatatypeDescriptor"), {nc::make_nc_method_result_datatype_descriptor_datatype()} }, - { U("NcMethodResultError"), {nc::make_nc_method_result_error_datatype()} }, - { U("NcDatatypeDescriptorEnum"), {nc::make_nc_datatype_descriptor_enum_datatype()} }, - { U("NcDatatypeDescriptorPrimitive"), {nc::make_nc_datatype_descriptor_primitive_datatype()} }, - { U("NcDatatypeDescriptorStruct"), {nc::make_nc_datatype_descriptor_struct_datatype()} }, - { U("NcDatatypeDescriptorTypeDef"), {nc::make_nc_datatype_descriptor_type_def_datatype()} }, - { U("NcEnumItemDescriptor"), {nc::make_nc_enum_item_descriptor_datatype()} }, - { U("NcFieldDescriptor"), {nc::make_nc_field_descriptor_datatype()} }, - { U("NcPropertyConstraintsNumber"), {nc::make_nc_property_constraints_number_datatype()} }, - { U("NcPropertyConstraintsString"), {nc::make_nc_property_constraints_string_datatype()} }, - { U("NcRegex"), {nc::make_nc_regex_datatype()} }, - { U("NcRolePath"), {nc::make_nc_role_path_datatype()} }, - { U("NcParameterConstraintsNumber"), {nc::make_nc_parameter_constraints_number_datatype()} }, - { U("NcParameterConstraintsString"), {nc::make_nc_parameter_constraints_string_datatype()} }, - { U("NcTimeInterval"), {nc::make_nc_time_interval_datatype()} }, - { U("NcTouchpointNmos"), {nc::make_nc_touchpoint_nmos_datatype()} }, - { U("NcTouchpointNmosChannelMapping"), {nc::make_nc_touchpoint_nmos_channel_mapping_datatype()} }, - { U("NcTouchpointResource"), {nc::make_nc_touchpoint_resource_datatype()} }, - { U("NcTouchpointResourceNmos"), {nc::make_nc_touchpoint_resource_nmos_datatype()} }, - { U("NcTouchpointResourceNmosChannelMapping"), {nc::make_nc_touchpoint_resource_nmos_channel_mapping_datatype()} }, + { U("NcBoolean"), {nc::make_boolean_datatype()} }, + { U("NcInt16"), {nc::make_int16_datatype()} }, + { U("NcInt32"), {nc::make_int32_datatype()} }, + { U("NcInt64"), {nc::make_int64_datatype()} }, + { U("NcUint16"), {nc::make_uint16_datatype()} }, + { U("NcUint32"), {nc::make_uint32_datatype()} }, + { U("NcUint64"), {nc::make_uint64_datatype()} }, + { U("NcFloat32"), {nc::make_float32_datatype()} }, + { U("NcFloat64"), {nc::make_float64_datatype()} }, + { U("NcString"), {nc::make_string_datatype()} }, + { U("NcClassId"), {nc::make_class_id_datatype()} }, + { U("NcOid"), {nc::make_oid_datatype()} }, + { U("NcTouchpoint"), {nc::make_touchpoint_datatype()} }, + { U("NcElementId"), {nc::make_element_id_datatype()} }, + { U("NcPropertyId"), {nc::make_property_id_datatype()} }, + { U("NcPropertyConstraints"), {nc::make_property_contraints_datatype()} }, + { U("NcMethodResultPropertyValue"), {nc::make_method_result_property_value_datatype()} }, + { U("NcMethodStatus"), {nc::make_method_status_datatype()} }, + { U("NcMethodResult"), {nc::make_method_result_datatype()} }, + { U("NcId"), {nc::make_id_datatype()} }, + { U("NcMethodResultId"), {nc::make_method_result_id_datatype()} }, + { U("NcMethodResultLength"), {nc::make_method_result_length_datatype()} }, + { U("NcPropertyChangeType"), {nc::make_property_change_type_datatype()} }, + { U("NcPropertyChangedEventData"), {nc::make_property_changed_event_data_datatype()} }, + { U("NcDescriptor"), {nc::make_descriptor_datatype()} }, + { U("NcBlockMemberDescriptor"), {nc::make_block_member_descriptor_datatype()} }, + { U("NcMethodResultBlockMemberDescriptors"), {nc::make_method_result_block_member_descriptors_datatype()} }, + { U("NcVersionCode"), {nc::make_version_code_datatype()} }, + { U("NcOrganizationId"), {nc::make_organization_id_datatype()} }, + { U("NcUri"), {nc::make_uri_datatype()} }, + { U("NcManufacturer"), {nc::make_manufacturer_datatype()} }, + { U("NcUuid"), {nc::make_uuid_datatype()} }, + { U("NcProduct"), {nc::make_product_datatype()} }, + { U("NcDeviceGenericState"), {nc::make_device_generic_state_datatype()} }, + { U("NcDeviceOperationalState"), {nc::make_device_operational_state_datatype()} }, + { U("NcResetCause"), {nc::make_reset_cause_datatype()} }, + { U("NcName"), {nc::make_name_datatype()} }, + { U("NcPropertyDescriptor"), {nc::make_property_descriptor_datatype()} }, + { U("NcParameterDescriptor"), {nc::make_parameter_descriptor_datatype()} }, + { U("NcMethodId"), {nc::make_method_id_datatype()} }, + { U("NcMethodDescriptor"), {nc::make_method_descriptor_datatype()} }, + { U("NcEventId"), {nc::make_event_id_datatype()} }, + { U("NcEventDescriptor"), {nc::make_event_descriptor_datatype()} }, + { U("NcClassDescriptor"), {nc::make_class_descriptor_datatype()} }, + { U("NcParameterConstraints"), {nc::make_parameter_constraints_datatype()} }, + { U("NcDatatypeType"), {nc::make_datatype_type_datatype()} }, + { U("NcDatatypeDescriptor"), {nc::make_datatype_descriptor_datatype()} }, + { U("NcMethodResultClassDescriptor"), {nc::make_method_result_class_descriptor_datatype()} }, + { U("NcMethodResultDatatypeDescriptor"), {nc::make_method_result_datatype_descriptor_datatype()} }, + { U("NcMethodResultError"), {nc::make_method_result_error_datatype()} }, + { U("NcDatatypeDescriptorEnum"), {nc::make_datatype_descriptor_enum_datatype()} }, + { U("NcDatatypeDescriptorPrimitive"), {nc::make_datatype_descriptor_primitive_datatype()} }, + { U("NcDatatypeDescriptorStruct"), {nc::make_datatype_descriptor_struct_datatype()} }, + { U("NcDatatypeDescriptorTypeDef"), {nc::make_datatype_descriptor_type_def_datatype()} }, + { U("NcEnumItemDescriptor"), {nc::make_enum_item_descriptor_datatype()} }, + { U("NcFieldDescriptor"), {nc::make_field_descriptor_datatype()} }, + { U("NcPropertyConstraintsNumber"), {nc::make_property_constraints_number_datatype()} }, + { U("NcPropertyConstraintsString"), {nc::make_property_constraints_string_datatype()} }, + { U("NcRegex"), {nc::make_regex_datatype()} }, + { U("NcRolePath"), {nc::make_role_path_datatype()} }, + { U("NcParameterConstraintsNumber"), {nc::make_parameter_constraints_number_datatype()} }, + { U("NcParameterConstraintsString"), {nc::make_parameter_constraints_string_datatype()} }, + { U("NcTimeInterval"), {nc::make_time_interval_datatype()} }, + { U("NcTouchpointNmos"), {nc::make_touchpoint_nmos_datatype()} }, + { U("NcTouchpointNmosChannelMapping"), {nc::make_touchpoint_nmos_channel_mapping_datatype()} }, + { U("NcTouchpointResource"), {nc::make_touchpoint_resource_datatype()} }, + { U("NcTouchpointResourceNmos"), {nc::make_touchpoint_resource_nmos_datatype()} }, + { U("NcTouchpointResourceNmosChannelMapping"), {nc::make_touchpoint_resource_nmos_channel_mapping_datatype()} }, // Monitoring feature set // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/monitoring/#datatypes - { U("NcConnectionStatus"), {nc::make_nc_connection_status_datatype()} }, - { U("NcCounter"), {nc::make_nc_counter_datatype()} }, - { U("NcEssenceStatus"), {nc::make_nc_essence_status_datatype()} }, - { U("NcLinkStatus"), {nc::make_nc_link_status_datatype()} }, - { U("NcMethodResultCounters"), {nc::make_nc_method_result_counters_datatype()} }, - { U("NcOverallStatus"), {nc::make_nc_overall_status_datatype()} }, - { U("NcSynchronizationStatus"), {nc::make_nc_synchronization_status_datatype()} }, - { U("NcStreamStatus"), {nc::make_nc_stream_status_datatype()} }, - { U("NcTransmissionStatus"), {nc::make_nc_transmission_status_datatype()} }, + { U("NcConnectionStatus"), {nc::make_connection_status_datatype()} }, + { U("NcCounter"), {nc::make_counter_datatype()} }, + { U("NcEssenceStatus"), {nc::make_essence_status_datatype()} }, + { U("NcLinkStatus"), {nc::make_link_status_datatype()} }, + { U("NcMethodResultCounters"), {nc::make_method_result_counters_datatype()} }, + { U("NcOverallStatus"), {nc::make_overall_status_datatype()} }, + { U("NcSynchronizationStatus"), {nc::make_synchronization_status_datatype()} }, + { U("NcStreamStatus"), {nc::make_stream_status_datatype()} }, + { U("NcTransmissionStatus"), {nc::make_transmission_status_datatype()} }, // Device configuration feature set // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#datatypes - { U("NcRestoreMode"), {nc::make_nc_restore_mode_datatype()} }, - { U("NcPropertyHolder"), {nc::make_nc_property_holder_datatype()} }, - { U("NcObjectPropertiesHolder"), {nc::make_nc_object_properties_holder_datatype()} }, - { U("NcBulkPropertiesHolder"), {nc::make_nc_bulk_properties_holder_datatype()} }, - { U("NcRestoreValidationStatus"), {nc::make_nc_restore_validation_status_datatype()} }, - { U("NcPropertyRestoreNoticeType"), {nc::make_nc_property_restore_notice_type_datatype()} }, - { U("NcPropertyRestoreNotice"), {nc::make_nc_property_restore_notice_datatype()} }, - { U("NcObjectPropertiesSetValidation"), {nc::make_nc_object_properties_set_validation_datatype()} }, - { U("NcMethodResultBulkPropertiesHolder"), {nc::make_nc_method_result_bulk_properties_holder_datatype()} }, - { U("NcMethodResultObjectPropertiesSetValidation"), {nc::make_nc_method_result_object_properties_set_validation_datatype()} } + { U("NcRestoreMode"), {nc::make_restore_mode_datatype()} }, + { U("NcPropertyHolder"), {nc::make_property_holder_datatype()} }, + { U("NcObjectPropertiesHolder"), {nc::make_object_properties_holder_datatype()} }, + { U("NcBulkPropertiesHolder"), {nc::make_bulk_properties_holder_datatype()} }, + { U("NcRestoreValidationStatus"), {nc::make_restore_validation_status_datatype()} }, + { U("NcPropertyRestoreNoticeType"), {nc::make_property_restore_notice_type_datatype()} }, + { U("NcPropertyRestoreNotice"), {nc::make_property_restore_notice_datatype()} }, + { U("NcObjectPropertiesSetValidation"), {nc::make_object_properties_set_validation_datatype()} }, + { U("NcMethodResultBulkPropertiesHolder"), {nc::make_method_result_bulk_properties_holder_datatype()} }, + { U("NcMethodResultObjectPropertiesSetValidation"), {nc::make_method_result_object_properties_set_validation_datatype()} } }; } diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index 0d4de4529..fc1416c76 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -40,7 +40,7 @@ namespace nmos auto& runtime_prop_constraints = runtime_property_constraints.as_array(); auto found_constraints = std::find_if(runtime_prop_constraints.begin(), runtime_prop_constraints.end(), [&property_id](const web::json::value& constraints) { - return property_id == parse_nc_property_id(nmos::fields::nc::property_id(constraints)); + return property_id == parse_property_id(nmos::fields::nc::property_id(constraints)); }); if (runtime_prop_constraints.end() != found_constraints) @@ -362,7 +362,7 @@ namespace nmos } // get the role_path_segement member resource - if (is_block(parse_nc_class_id(nmos::fields::nc::class_id(*member_found)))) + if (is_block(parse_class_id(nmos::fields::nc::class_id(*member_found)))) { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(*member_found); @@ -414,7 +414,7 @@ namespace nmos const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != found) { - if (nmos::nc::is_sender_monitor(parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) + if (nmos::nc::is_sender_monitor(parse_class_id(nmos::fields::nc::class_id(found->data)))) { return details::update_sender_monitor_overall_status(resources, oid, get_control_protocol_class_descriptor, gate); } @@ -690,7 +690,7 @@ namespace nmos const auto& property_descriptors = control_class.property_descriptors.as_array(); auto found = std::find_if(property_descriptors.begin(), property_descriptors.end(), [&property_id](const web::json::value& property_descriptor) { - return (property_id == nc::details::parse_nc_property_id(nmos::fields::nc::id(property_descriptor))); + return (property_id == nc::details::parse_property_id(nmos::fields::nc::id(property_descriptor))); }); if (property_descriptors.end() != found) { return *found; } @@ -717,7 +717,7 @@ namespace nmos // get members on all NcBlock(s) for (const auto& member : members) { - if (is_block(nc::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + if (is_block(nc::details::parse_class_id(nmos::fields::nc::class_id(member)))) { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(member); @@ -771,7 +771,7 @@ namespace nmos // do role match on all NcBlock(s) for (const auto& member : members) { - if (is_block(nc::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + if (is_block(nc::details::parse_class_id(nmos::fields::nc::class_id(member)))) { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(member); @@ -795,7 +795,7 @@ namespace nmos auto match = [&](const web::json::value& descriptor) { - const auto& class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(descriptor)); + const auto& class_id = nc::details::parse_class_id(nmos::fields::nc::class_id(descriptor)); if (include_derived) { return !boost::find_first(class_id, class_id_).empty(); } else { return class_id == class_id_; } @@ -819,7 +819,7 @@ namespace nmos // do class_id match on all NcBlock(s) for (const auto& member : members) { - if (is_block(nc::details::parse_nc_class_id(nmos::fields::nc::class_id(member)))) + if (is_block(nc::details::parse_class_id(nmos::fields::nc::class_id(member)))) { // get resource based on the oid const auto& oid = nmos::fields::nc::oid(member); @@ -842,10 +842,10 @@ namespace nmos auto& parent = nc_block_resource.data; const auto& child = resource.data; - if (!is_block(details::parse_nc_class_id(nmos::fields::nc::class_id(parent)))) throw std::logic_error("non-NcBlock cannot be nested"); + if (!is_block(details::parse_class_id(nmos::fields::nc::class_id(parent)))) throw std::logic_error("non-NcBlock cannot be nested"); web::json::push_back(parent[nmos::fields::nc::members], - details::make_nc_block_member_descriptor(nmos::fields::description(child), nmos::fields::nc::role(child), nmos::fields::nc::oid(child), nmos::fields::nc::constant_oid(child), nc::details::parse_nc_class_id(nmos::fields::nc::class_id(child)), nmos::fields::nc::user_label(child), nmos::fields::nc::oid(parent))); + details::make_block_member_descriptor(nmos::fields::description(child), nmos::fields::nc::role(child), nmos::fields::nc::oid(child), nmos::fields::nc::constant_oid(child), nc::details::parse_class_id(nmos::fields::nc::class_id(child)), nmos::fields::nc::user_label(child), nmos::fields::nc::oid(parent))); nc_block_resource.resources.push_back(resource); } @@ -1095,7 +1095,7 @@ namespace nmos if (resources.end() != found) { // find the relevant nc_property_descriptor - const auto& property = nc::find_property_descriptor(property_id, nc::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(property_id, nc::details::parse_class_id(nmos::fields::nc::class_id(found->data)), get_control_protocol_class_descriptor); if (!property.is_null() && found->has_data() && found->data.has_field(nmos::fields::nc::name(property))) { return found->data.at(nmos::fields::nc::name(property)); @@ -1111,7 +1111,7 @@ namespace nmos const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != found) { - const auto& property = nc::find_property_descriptor(property_id, nc::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)), get_control_protocol_class_descriptor); + const auto& property = nc::find_property_descriptor(property_id, nc::details::parse_class_id(nmos::fields::nc::class_id(found->data)), get_control_protocol_class_descriptor); if (!property.is_null()) { try @@ -1302,9 +1302,9 @@ namespace nmos // Furthermore, after activation, as long as the monitor isn’t being deactivated, it MUST delay the reporting // of non Healthy states for the duration specified by statusReportingDelay, and then transition to any other appropriate state. const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found && nc::is_status_monitor(nc::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) + if (resources.end() != found && nc::is_status_monitor(nc::details::parse_class_id(nmos::fields::nc::class_id(found->data)))) { - const auto& class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)); + const auto& class_id = nc::details::parse_class_id(nmos::fields::nc::class_id(found->data)); auto activation_time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); auto succeed = set_property(resources, oid, nmos::fields::nc::monitor_activation_time, activation_time, gate); @@ -1361,9 +1361,9 @@ namespace nmos bool deactivate_monitor(resources& resources, nc_oid oid, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); - if (resources.end() != found && nc::is_status_monitor(nc::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)))) + if (resources.end() != found && nc::is_status_monitor(nc::details::parse_class_id(nmos::fields::nc::class_id(found->data)))) { - const auto& class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(found->data)); + const auto& class_id = nc::details::parse_class_id(nmos::fields::nc::class_id(found->data)); auto succeed = set_property(resources, oid, nmos::fields::nc::monitor_activation_time, web::json::value::number(0), gate); diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index f8d39a6ec..ddc4b59db 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -246,7 +246,7 @@ namespace nmos const auto oid = nmos::fields::nc::oid(cmd); // get methodId - const auto& method_id = nc::details::parse_nc_method_id(nmos::fields::nc::method_id(cmd)); + const auto& method_id = nc::details::parse_method_id(nmos::fields::nc::method_id(cmd)); // get arguments const auto& arguments = nmos::fields::nc::arguments(cmd); @@ -256,7 +256,7 @@ namespace nmos auto resource = nmos::find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != resource) { - const auto class_id = nc::details::parse_nc_class_id(nmos::fields::nc::class_id(resource->data)); + const auto class_id = nc::details::parse_class_id(nmos::fields::nc::class_id(resource->data)); // find the relevant method handler to execute // method tuple definition described in control_protocol_handlers.h @@ -280,7 +280,7 @@ namespace nmos utility::ostringstream_t ss; ss << "invalid argument: " << arguments.serialize() << " error: " << e.what(); slog::log(gate, SLOG_FLF) << ss.str(); - nc_method_result = nc::details::make_nc_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); + nc_method_result = nc::details::make_method_result_error({ nmos::nc_method_status::parameter_error }, ss.str()); } } else @@ -290,7 +290,7 @@ namespace nmos ss << U("unsupported method_id: ") << nmos::fields::nc::method_id(cmd).serialize() << U(" for control class class_id: ") << resource->data.at(nmos::fields::nc::class_id).serialize(); slog::log(gate, SLOG_FLF) << ss.str(); - nc_method_result = nc::details::make_nc_method_result_error({ nc_method_status::method_not_implemented }, ss.str()); + nc_method_result = nc::details::make_method_result_error({ nc_method_status::method_not_implemented }, ss.str()); } } else @@ -299,7 +299,7 @@ namespace nmos utility::ostringstream_t ss; ss << U("unknown oid: ") << oid; slog::log(gate, SLOG_FLF) << ss.str(); - nc_method_result = nc::details::make_nc_method_result_error({ nc_method_status::bad_oid }, ss.str()); + nc_method_result = nc::details::make_method_result_error({ nc_method_status::bad_oid }, ss.str()); } // accumulating up response auto response = nc::make_control_protocol_response(handle, nc_method_result); diff --git a/Development/nmos/test/configuration_methods_test.cpp b/Development/nmos/test/configuration_methods_test.cpp index 4f954521f..f360777bb 100644 --- a/Development/nmos/test/configuration_methods_test.cpp +++ b/Development/nmos/test/configuration_methods_test.cpp @@ -42,13 +42,13 @@ BST_TEST_CASE(testGetPropertiesByPath) auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); nmos::make_rebuildable(receivers); // root, receivers, mon1 - auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({{nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({{nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); // make monitor1 rebuildable nmos::make_rebuildable(monitor1); - auto monitor_class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + auto monitor_class_id = nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 - auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); nmos::nc::push_back(receivers, monitor1); // add example-control to root-block nmos::nc::push_back(receivers, monitor2); diff --git a/Development/nmos/test/configuration_utils_test.cpp b/Development/nmos/test/configuration_utils_test.cpp index 0ced2c49a..55474e1ee 100644 --- a/Development/nmos/test/configuration_utils_test.cpp +++ b/Development/nmos/test/configuration_utils_test.cpp @@ -60,10 +60,10 @@ BST_TEST_CASE(testIsBlockModified) auto receivers = nmos::make_block(++oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); auto receiver_block_oid = oid; // root, receivers, mon1 - auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); auto monitor_1_oid = oid; // root, receivers, mon2 - auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); auto monitor_2_oid = oid; nmos::nc::push_back(receivers, monitor1); // add example-control to root-block @@ -76,25 +76,25 @@ BST_TEST_CASE(testIsBlockModified) push_back(role_path, U("root")); push_back(role_path, U("receivers")); - const auto block_members_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::nc::details::make_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); // Members unchanged { auto property_holders = value::array(); auto members = value::array(); - const auto class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + const auto class_id = nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(monitor1.data)); { - const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); push_back(members, block_member_descriptor); } { - const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); + const auto property_holder = nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(!nmos::is_block_modified(receivers, object_properties_holder)); } @@ -104,12 +104,12 @@ BST_TEST_CASE(testIsBlockModified) auto property_holders = value::array(); auto members = value::array(); - const auto class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); - const auto block_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, class_id, U("label"), receiver_block_oid); + const auto class_id = nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(monitor1.data)); + const auto block_descriptor = nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("monitor 1"), monitor_1_oid, true, class_id, U("label"), receiver_block_oid); push_back(members, block_descriptor); - const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); + const auto property_holder = nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -119,18 +119,18 @@ BST_TEST_CASE(testIsBlockModified) auto property_holders = value::array(); auto members = value::array(); - const auto class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + const auto class_id = nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(monitor1.data)); { - const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), 10, true, class_id, U("monitor 1"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), 10, true, class_id, U("monitor 1"), receiver_block_oid); push_back(members, block_member_descriptor); } { - const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), 20, true, class_id, U("monitor 2"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_block_member_descriptor(U("monitor 2"), U("mon2"), 20, true, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); + const auto property_holder = nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -140,18 +140,18 @@ BST_TEST_CASE(testIsBlockModified) auto property_holders = value::array(); auto members = value::array(); - const auto class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + const auto class_id = nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(monitor1.data)); { - const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon3"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon3"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); push_back(members, block_member_descriptor); } { - const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon4"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_block_member_descriptor(U("monitor 2"), U("mon4"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); + const auto property_holder = nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -163,16 +163,16 @@ BST_TEST_CASE(testIsBlockModified) auto members = value::array(); const auto class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 1 }); { - const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), receiver_block_oid); push_back(members, block_member_descriptor); } { - const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); + const auto property_holder = nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -184,16 +184,16 @@ BST_TEST_CASE(testIsBlockModified) auto members = value::array(); const auto class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 1 }); { - const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), 20); + const auto block_member_descriptor = nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, class_id, U("monitor 1"), 20); push_back(members, block_member_descriptor); } { - const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), 20); + const auto block_member_descriptor = nmos::nc::details::make_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, class_id, U("monitor 2"), 20); push_back(members, block_member_descriptor); } - const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); + const auto property_holder = nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -205,16 +205,16 @@ BST_TEST_CASE(testIsBlockModified) auto members = value::array(); const auto class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 1 }); { - const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, false, class_id, U("monitor 1"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, false, class_id, U("monitor 1"), receiver_block_oid); push_back(members, block_member_descriptor); } { - const auto block_member_descriptor = nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, false, class_id, U("monitor 2"), receiver_block_oid); + const auto block_member_descriptor = nmos::nc::details::make_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, false, class_id, U("monitor 2"), receiver_block_oid); push_back(members, block_member_descriptor); } - const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); + const auto property_holder = nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members); web::json::push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); BST_CHECK(nmos::is_block_modified(receivers, object_properties_holder)); } @@ -226,7 +226,7 @@ BST_TEST_CASE(testGetObjectPropertiesHolder) using web::json::value_of; using web::json::value; - const auto enabled_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, web::json::value::null()); + const auto enabled_property_descriptor = nmos::nc::details::make_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, web::json::value::null()); // Create Object Properties Holder auto object_properties_holders = value::array(); @@ -234,25 +234,25 @@ BST_TEST_CASE(testGetObjectPropertiesHolder) { const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); - const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); + const auto property_holder = nmos::nc::details::make_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); } { const auto role_path = value_of({ U("root"), U("receivers"), U("mon2") }); auto property_holders = value::array(); - const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); + const auto property_holder = nmos::nc::details::make_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); } { const auto role_path = value_of({ U("root"), U("senders"), U("mon1") }); auto property_holders = value::array(); - const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); + const auto property_holder = nmos::nc::details::make_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); } @@ -300,10 +300,10 @@ BST_TEST_CASE(testGetRolePath) auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); nmos::make_rebuildable(receivers); // root, receivers, mon1 - auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); - nmos::nc_class_id monitor_class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + nmos::nc_class_id monitor_class_id = nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 - auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); nmos::nc::push_back(receivers, monitor1); // add example-control to root-block nmos::nc::push_back(receivers, monitor2); @@ -352,14 +352,14 @@ BST_TEST_CASE(testApplyBackupDataSet) auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); nmos::make_rebuildable(receivers); // root, receivers, mon1 - auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({{nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({{nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); // make monitor1 rebuildable nmos::make_rebuildable(monitor1); auto monitor_1_oid = oid; - auto monitor_class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + auto monitor_class_id = nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 - auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); auto monitor_2_oid = oid; nmos::nc::push_back(receivers, monitor1); // add example-control to root-block @@ -396,13 +396,13 @@ BST_TEST_CASE(testApplyBackupDataSet) create_device_model_object_called = true; - auto data = nmos::nc::details::make_nc_object(nmos::nc_receiver_monitor_class_id, oid, true, owner, role, web::json::value::string(user_label), U(""), web::json::value::null(), web::json::value::null()); + auto data = nmos::nc::details::make_object(nmos::nc_receiver_monitor_class_id, oid, true, owner, role, web::json::value::string(user_label), U(""), web::json::value::null(), web::json::value::null()); return nmos::control_protocol_resource({ nmos::is12_versions::v1_0, nmos::types::nc_block, std::move(data), true }); }; - const auto enabled_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, web::json::value::null()); - const auto class_id_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, web::json::value::null()); + const auto enabled_property_descriptor = nmos::nc::details::make_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, web::json::value::null()); + const auto class_id_property_descriptor = nmos::nc::details::make_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, web::json::value::null()); { // Check the successful modification of the "enabled" flag of mon1's worker base class in Modify mode // @@ -410,9 +410,9 @@ BST_TEST_CASE(testApplyBackupDataSet) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); - const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); + const auto property_holder = nmos::nc::details::make_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); const auto target_role_path = value_of({ U("root"), U("receivers")}); bool recurse = true; @@ -435,7 +435,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(!remove_device_model_object_called); BST_CHECK(!create_device_model_object_called); } - const auto connection_status_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::fields::nc::connection_status_message, U("NcString"), true, false, false, false, web::json::value::null()); + const auto connection_status_property_descriptor = nmos::nc::details::make_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::fields::nc::connection_status_message, U("NcString"), true, false, false, false, web::json::value::null()); { // Check get_read_only_modification_allow_list_handler is called when changing a read only property of rebuildable object in Rebuild mode // @@ -448,9 +448,9 @@ BST_TEST_CASE(testApplyBackupDataSet) const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); // This is a read only property - const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value(U("change this value"))); + const auto property_holder = nmos::nc::details::make_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value(U("change this value"))); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -487,9 +487,9 @@ BST_TEST_CASE(testApplyBackupDataSet) const auto role_path = value_of({ U("root"), U("receivers"), U("mon2") }); auto property_holders = value::array(); // This is a read only property - const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value(U("change this value"))); + const auto property_holder = nmos::nc::details::make_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value(U("change this value"))); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -513,7 +513,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_REQUIRE_EQUAL(1, property_restore_notices.size()); const auto& notice = *property_restore_notices.begin(); - BST_CHECK_EQUAL(nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::nc::details::parse_nc_property_id(nmos::fields::nc::id(notice))); + BST_CHECK_EQUAL(nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::nc::details::parse_property_id(nmos::fields::nc::id(notice))); BST_CHECK_EQUAL(nmos::fields::nc::connection_status_message.key, nmos::fields::nc::name(notice)); BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); @@ -537,10 +537,10 @@ BST_TEST_CASE(testApplyBackupDataSet) const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); // This is a read only property - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value(U("change this value")))); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, connection_status_property_descriptor, value(U("change this value")))); // This is a writable property - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false))); - const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false))); + const auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -565,7 +565,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_REQUIRE_EQUAL(1, property_restore_notices.size()); const auto& notice = *property_restore_notices.begin(); - BST_CHECK_EQUAL(nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::nc::details::parse_nc_property_id(nmos::fields::nc::id(notice))); + BST_CHECK_EQUAL(nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::nc::details::parse_property_id(nmos::fields::nc::id(notice))); BST_CHECK_EQUAL(nmos::fields::nc::connection_status_message.key, nmos::fields::nc::name(notice)); BST_CHECK_EQUAL(nmos::nc_property_restore_notice_type::error, nmos::fields::nc::notice_type(notice)); BST_CHECK_NE(U(""), nmos::fields::nc::notice_message(notice)); @@ -574,7 +574,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(!remove_device_model_object_called); BST_CHECK(!create_device_model_object_called); } - const auto block_members_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::nc::details::make_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); { // Check remove_device_model_object_called is called when trying to modify a rebuildable block // @@ -588,9 +588,9 @@ BST_TEST_CASE(testApplyBackupDataSet) auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; @@ -611,7 +611,7 @@ BST_TEST_CASE(testApplyBackupDataSet) BST_CHECK(remove_device_model_object_called); BST_CHECK(!create_device_model_object_called); } - const auto oid_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::nc::details::make_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); { // Check create_device_model_object_called is called when trying to modify a rebuildable block // @@ -626,25 +626,25 @@ BST_TEST_CASE(testApplyBackupDataSet) { auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_class_id(nmos::nc_receiver_monitor_class_id))); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -697,9 +697,9 @@ BST_TEST_CASE(testApplyBackupDataSet) auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; @@ -739,12 +739,12 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); nmos::make_rebuildable(receivers); // root, receivers, mon1 - auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); nmos::make_rebuildable(monitor1); auto monitor_1_oid = oid; - const auto monitor_class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + const auto monitor_class_id = nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 - auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); nmos::nc::push_back(receivers, monitor1); // add example-control to root-block nmos::nc::push_back(receivers, monitor2); @@ -761,7 +761,7 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) nmos::remove_device_model_object_handler remove_device_model_object; nmos::create_device_model_object_handler create_device_model_object; - const auto enabled_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, web::json::value::null()); + const auto enabled_property_descriptor = nmos::nc::details::make_property_descriptor(U("enabled"), nmos::nc_worker_enabled_property_id, nmos::fields::nc::enabled, U("NcBoolean"), false, false, false, false, web::json::value::null()); { // Check that Modify mode is unaffected by undefined Rebuild mode callbacks // @@ -769,9 +769,9 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); - const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); + const auto property_holder = nmos::nc::details::make_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -796,9 +796,9 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto object_properties_holders = value::array(); const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); - const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); + const auto property_holder = nmos::nc::details::make_property_holder(nmos::nc_worker_enabled_property_id, enabled_property_descriptor, value::boolean(false)); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -824,10 +824,10 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) const auto role_path = value_of({ U("root"), U("receivers"), U("mon1") }); auto property_holders = value::array(); // This is a read only property - const auto property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::fields::nc::connection_status_message, U("NcString"), true, false, false, false, web::json::value::null()); - const auto property_holder = nmos::nc::details::make_nc_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, property_descriptor, value(U("change this value"))); + const auto property_descriptor = nmos::nc::details::make_property_descriptor(U("connectionStatusMessage"), nmos::nc_receiver_monitor_connection_status_message_property_id, nmos::fields::nc::connection_status_message, U("NcString"), true, false, false, false, web::json::value::null()); + const auto property_holder = nmos::nc::details::make_property_holder(nmos::nc_receiver_monitor_connection_status_message_property_id, property_descriptor, value(U("change this value"))); push_back(property_holders, property_holder); - const auto object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, object_properties_holder); // must be a more efficient way of initializing these role paths const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -847,8 +847,8 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) BST_CHECK_EQUAL(nmos::nc_restore_validation_status::failed, nmos::fields::nc::status(object_properties_set_validation)); } { - const auto block_members_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); - const auto oid_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::nc::details::make_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::nc::details::make_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); // Check undefined create_device_model_object and remove_device_model_object causes an unsupported error when attempting to modify a rebuildable block // // Create Object Properties Holder @@ -857,14 +857,14 @@ BST_TEST_CASE(testApplyBackupDataSet_WithoutCallbacks) auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -915,14 +915,14 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); nmos::make_rebuildable(receivers); // root, receivers, mon1 - auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({{nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({{nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})}})); // make monitor1 rebuildable nmos::make_rebuildable(monitor1); auto monitor_1_oid = oid; - auto monitor_class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + auto monitor_class_id = nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 - auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); auto monitor_2_oid = oid; nmos::nc::push_back(receivers, monitor1); // add example-control to root-block @@ -959,14 +959,14 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) create_device_model_object_called = true; - auto data = nmos::nc::details::make_nc_object(nmos::nc_receiver_monitor_class_id, oid, true, owner, role, web::json::value::string(user_label), U(""), web::json::value::null(), web::json::value::null()); + auto data = nmos::nc::details::make_object(nmos::nc_receiver_monitor_class_id, oid, true, owner, role, web::json::value::string(user_label), U(""), web::json::value::null(), web::json::value::null()); return nmos::control_protocol_resource({ nmos::is12_versions::v1_0, nmos::types::nc_block, std::move(data), true }); }; - const auto block_members_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); - const auto oid_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); - const auto class_id_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::nc::details::make_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::nc::details::make_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); + const auto class_id_property_descriptor = nmos::nc::details::make_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, web::json::value::null()); { // Handle constant oid clash // @@ -980,25 +980,25 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) { auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_class_id(nmos::nc_receiver_monitor_class_id))); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -1049,25 +1049,25 @@ BST_TEST_CASE(testApplyBackupDataSet_AddDeviceModelObject) { auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, false, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, false, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_class_id(nmos::nc_receiver_monitor_class_id))); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -1142,14 +1142,14 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) auto receivers = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receivers"), U("Receivers block")); nmos::make_rebuildable(receivers); // root, receivers, mon1 - auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); // make monitor1 rebuildable nmos::make_rebuildable(monitor1); auto monitor_1_oid = oid; - auto monitor_class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + auto monitor_class_id = nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 - auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); auto monitor_2_oid = oid; nmos::nc::push_back(receivers, monitor1); // add example-control to root-block @@ -1187,8 +1187,8 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) // Simulate error on adding object to device model return nmos::control_protocol_resource(); }; - const auto block_members_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); - const auto oid_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::nc::details::make_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::nc::details::make_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); { // Check remove_device_model_object_called error is handled when attempting to modify a rebuildable block // @@ -1202,9 +1202,9 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; bool validate = true; @@ -1243,14 +1243,14 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -1285,7 +1285,7 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) BST_CHECK_EQUAL(0, nmos::fields::nc::notices(object_properties_set_validation).size()); } } - const auto class_id_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, U("classId"), U("NcClassId"), true, false, false, false, web::json::value::null()); + const auto class_id_property_descriptor = nmos::nc::details::make_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, U("classId"), U("NcClassId"), true, false, false, false, web::json::value::null()); { // Check create_device_model_object_called error is handled when trying to modify a rebuildable block // @@ -1300,31 +1300,31 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) { auto property_holders = value::array(); auto members = value::array(); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_class_id(nmos::nc_receiver_monitor_class_id))); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_3_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool recurse = true; @@ -1386,18 +1386,18 @@ BST_TEST_CASE(testApplyBackupDataSet_NegativeTests) auto property_holders2 = value::array(); auto members2 = value::array(); - push_back(members1, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders1, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members1)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders1.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members1, nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders1, nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members1)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders1.as_array(), value::array().as_array(), value::array().as_array(), false)); // duplicate - push_back(members2, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(property_holders2, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members2)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), property_holders2.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(members2, nmos::nc::details::make_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(property_holders2, nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members2)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(role_path.as_array(), property_holders2.as_array(), value::array().as_array(), value::array().as_array(), false)); const auto monitor_1_role_path = value_of({ U("root"), U("receivers"), U("mon1") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_1_oid)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_1_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); @@ -1451,14 +1451,14 @@ BST_TEST_CASE(testModifyRebuildableBlock) nmos::make_rebuildable(receivers); nmos::set_block_allowed_member_classes(receivers, {nmos::nc_receiver_monitor_class_id}); // root, receivers, mon1 - auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_1")})} })); // make monitor1 rebuildable nmos::make_rebuildable(monitor1); auto monitor_1_oid = oid; - auto monitor_class_id = nmos::nc::details::parse_nc_class_id(nmos::fields::nc::class_id(monitor1.data)); + auto monitor_class_id = nmos::nc::details::parse_class_id(nmos::fields::nc::class_id(monitor1.data)); // root, receivers, mon2 - auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, U("id_2")})} })); auto monitor_2_oid = oid; nmos::nc::push_back(receivers, monitor1); // add example-control to root-block @@ -1495,13 +1495,13 @@ BST_TEST_CASE(testModifyRebuildableBlock) create_device_model_object_called = true; - auto data = nmos::nc::details::make_nc_object(nmos::nc_receiver_monitor_class_id, oid, true, owner, role, web::json::value::string(user_label), U(""), web::json::value::null(), web::json::value::null()); + auto data = nmos::nc::details::make_object(nmos::nc_receiver_monitor_class_id, oid, true, owner, role, web::json::value::string(user_label), U(""), web::json::value::null(), web::json::value::null()); return nmos::control_protocol_resource({ nmos::is12_versions::v1_0, nmos::types::nc_block, std::move(data), true }); }; - const auto block_members_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); - const auto oid_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); - const auto class_id_property_descriptor = nmos::nc::details::make_nc_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, web::json::value::null()); + const auto block_members_property_descriptor = nmos::nc::details::make_property_descriptor(U("members"), nmos::nc_block_members_property_id, nmos::fields::nc::members, U("NcBlockMemberDescriptor"), true, false, true, false, web::json::value::null()); + const auto oid_property_descriptor = nmos::nc::details::make_property_descriptor(U("oid"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, web::json::value::null()); + const auto class_id_property_descriptor = nmos::nc::details::make_property_descriptor(U("classId"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, web::json::value::null()); // No class id specified in the objet properties holder for new monitor causes an error { @@ -1512,26 +1512,26 @@ BST_TEST_CASE(testModifyRebuildableBlock) const auto role_path = value_of({ U("root"), U("receivers") }); { auto members = value::array(); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(block_property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(block_property_holders, nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); } - const auto block_object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto block_object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, block_object_properties_holder); const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } // Create Object Properties Holder for new monitor auto monitor3_property_holders = value::array(); const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { // No property holders, including no class id - push_back(monitor3_property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(monitor3_property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool validate = true; @@ -1571,27 +1571,27 @@ BST_TEST_CASE(testModifyRebuildableBlock) const auto role_path = value_of({ U("root"), U("receivers") }); { auto members = value::array(); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(block_property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(block_property_holders, nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); } - const auto block_object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto block_object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, block_object_properties_holder); // Create Object Properties Holder for new monitor const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } auto monitor3_property_holders = value::array(); const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { //auto property_holders = value::array(); - push_back(monitor3_property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); - push_back(monitor3_property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_nc_class_id(nmos::nc_block_class_id))); // disallowed class id - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(monitor3_property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(monitor3_property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_class_id(nmos::nc_block_class_id))); // disallowed class id + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool validate = true; @@ -1630,27 +1630,27 @@ BST_TEST_CASE(testModifyRebuildableBlock) const auto role_path = value_of({ U("root"), U("receivers") }); { auto members = value::array(); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(members, nmos::nc::details::make_nc_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); - push_back(block_property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 1"), U("mon1"), monitor_1_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 2"), U("mon2"), monitor_2_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(members, nmos::nc::details::make_block_member_descriptor(U("monitor 3"), U("mon3"), monitor_3_oid, true, monitor_class_id, U("label"), receiver_block_oid)); + push_back(block_property_holders, nmos::nc::details::make_property_holder(nmos::nc_block_members_property_id, block_members_property_descriptor, members)); } - const auto block_object_properties_holder = nmos::nc::details::make_nc_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); + const auto block_object_properties_holder = nmos::nc::details::make_object_properties_holder(role_path.as_array(), block_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false); push_back(object_properties_holders, block_object_properties_holder); // Create Object Properties Holder for new monitor const auto monitor_2_role_path = value_of({ U("root"), U("receivers"), U("mon2") }); { auto property_holders = value::array(); - push_back(property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_2_oid)); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_2_role_path.as_array(), property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } auto monitor3_property_holders = value::array(); const auto monitor_3_role_path = value_of({ U("root"), U("receivers"), U("mon3") }); { //auto property_holders = value::array(); - push_back(monitor3_property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); - push_back(monitor3_property_holders, nmos::nc::details::make_nc_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_nc_class_id(nmos::nc_receiver_monitor_class_id))); - push_back(object_properties_holders, nmos::nc::details::make_nc_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); + push_back(monitor3_property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_oid_property_id, oid_property_descriptor, monitor_3_oid)); + push_back(monitor3_property_holders, nmos::nc::details::make_property_holder(nmos::nc_object_class_id_property_id, class_id_property_descriptor, nmos::nc::details::make_class_id(nmos::nc_receiver_monitor_class_id))); + push_back(object_properties_holders, nmos::nc::details::make_object_properties_holder(monitor_3_role_path.as_array(), monitor3_property_holders.as_array(), value::array().as_array(), value::array().as_array(), false)); } const auto target_role_path = value_of({ U("root"), U("receivers") }); bool validate = true; diff --git a/Development/nmos/test/control_protocol_methods_test.cpp b/Development/nmos/test/control_protocol_methods_test.cpp index 0fbde5358..26c8fd2a8 100644 --- a/Development/nmos/test/control_protocol_methods_test.cpp +++ b/Development/nmos/test/control_protocol_methods_test.cpp @@ -53,7 +53,7 @@ BST_TEST_CASE(testRemoveSequenceItem) // helper function to create writable_sequence object auto make_writable_sequence = [&writable_value, &writable_sequence_class_id](nmos::nc_oid oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const utility::string_t& description) { - auto data = nmos::nc::details::make_nc_worker(writable_sequence_class_id, oid, true, owner, role, value::string(user_label), description, web::json::value::null(), web::json::value::null(), true); + auto data = nmos::nc::details::make_worker(writable_sequence_class_id, oid, true, owner, role, value::string(user_label), description, web::json::value::null(), web::json::value::null(), true); auto values = value::array(); web::json::push_back(values, value::number(10)); web::json::push_back(values, value::number(9)); diff --git a/Development/nmos/test/control_protocol_test.cpp b/Development/nmos/test/control_protocol_test.cpp index b53d4b436..b8f418d8d 100644 --- a/Development/nmos/test/control_protocol_test.cpp +++ b/Development/nmos/test/control_protocol_test.cpp @@ -29,7 +29,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false }, { U("constraints"), value::null() } }); - const auto property_class_id_ = nmos::nc::details::make_nc_property_descriptor(U("Static value. All instances of the same class will have the same identity value"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, value::null()); + const auto property_class_id_ = nmos::nc::details::make_property_descriptor(U("Static value. All instances of the same class will have the same identity value"), nmos::nc_object_class_id_property_id, nmos::fields::nc::class_id, U("NcClassId"), true, false, false, false, value::null()); BST_REQUIRE_EQUAL(property_class_id, property_class_id_); const auto property_oid = value_of({ @@ -46,7 +46,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false }, { U("constraints"), value::null() } }); - const auto property_oid_ = nmos::nc::details::make_nc_property_descriptor(U("Object identifier"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, value::null()); + const auto property_oid_ = nmos::nc::details::make_property_descriptor(U("Object identifier"), nmos::nc_object_oid_property_id, nmos::fields::nc::oid, U("NcOid"), true, false, false, false, value::null()); BST_REQUIRE_EQUAL(property_oid, property_oid_); const auto property_constant_oid = value_of({ @@ -63,7 +63,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false }, { U("constraints"), value::null() } }); - const auto property_constant_oid_ = nmos::nc::details::make_nc_property_descriptor(U("TRUE iff OID is hardwired into device"), nmos::nc_object_constant_oid_property_id, nmos::fields::nc::constant_oid, U("NcBoolean"), true, false, false, false, value::null()); + const auto property_constant_oid_ = nmos::nc::details::make_property_descriptor(U("TRUE iff OID is hardwired into device"), nmos::nc_object_constant_oid_property_id, nmos::fields::nc::constant_oid, U("NcBoolean"), true, false, false, false, value::null()); BST_REQUIRE_EQUAL(property_constant_oid, property_constant_oid_); const auto property_owner = value_of({ @@ -80,7 +80,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false }, { U("constraints"), value::null() } }); - const auto property_owner_ = nmos::nc::details::make_nc_property_descriptor(U("OID of containing block. Can only ever be null for the root block"), nmos::nc_object_owner_property_id, nmos::fields::nc::owner, U("NcOid"), true, true, false, false, value::null()); + const auto property_owner_ = nmos::nc::details::make_property_descriptor(U("OID of containing block. Can only ever be null for the root block"), nmos::nc_object_owner_property_id, nmos::fields::nc::owner, U("NcOid"), true, true, false, false, value::null()); BST_REQUIRE_EQUAL(property_owner, property_owner_); const auto property_role = value_of({ @@ -97,7 +97,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false }, { U("constraints"), value::null() } }); - const auto property_role_ = nmos::nc::details::make_nc_property_descriptor(U("Role of object in the containing block"), nmos::nc_object_role_property_id, nmos::fields::nc::role, U("NcString"), true, false, false, false, value::null()); + const auto property_role_ = nmos::nc::details::make_property_descriptor(U("Role of object in the containing block"), nmos::nc_object_role_property_id, nmos::fields::nc::role, U("NcString"), true, false, false, false, value::null()); BST_REQUIRE_EQUAL(property_role, property_role_); const auto property_user_label = value_of({ @@ -114,7 +114,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false }, { U("constraints"), value::null() } }); - const auto property_user_label_ = nmos::nc::details::make_nc_property_descriptor(U("Scribble strip"), nmos::nc_object_user_label_property_id, nmos::fields::nc::user_label, U("NcString"), false, true, false, false, value::null()); + const auto property_user_label_ = nmos::nc::details::make_property_descriptor(U("Scribble strip"), nmos::nc_object_user_label_property_id, nmos::fields::nc::user_label, U("NcString"), false, true, false, false, value::null()); BST_REQUIRE_EQUAL(property_user_label, property_user_label_); const auto property_touchpoints = value_of({ @@ -131,7 +131,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false }, { U("constraints"), value::null() } }); - const auto property_touchpoints_ = nmos::nc::details::make_nc_property_descriptor(U("Touchpoints to other contexts"), nmos::nc_object_touchpoints_property_id, nmos::fields::nc::touchpoints, U("NcTouchpoint"), true, true, true, false, value::null()); + const auto property_touchpoints_ = nmos::nc::details::make_property_descriptor(U("Touchpoints to other contexts"), nmos::nc_object_touchpoints_property_id, nmos::fields::nc::touchpoints, U("NcTouchpoint"), true, true, true, false, value::null()); BST_REQUIRE_EQUAL(property_touchpoints, property_touchpoints_); const auto property_runtime_property_constraints = value_of({ @@ -148,7 +148,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false }, { U("constraints"), value::null() } }); - const auto property_runtime_property_constraints_ = nmos::nc::details::make_nc_property_descriptor(U("Runtime property constraints"), nmos::nc_object_runtime_property_constraints_property_id, nmos::fields::nc::runtime_property_constraints, U("NcPropertyConstraints"), true, true, true, false, value::null()); + const auto property_runtime_property_constraints_ = nmos::nc::details::make_property_descriptor(U("Runtime property constraints"), nmos::nc_object_runtime_property_constraints_property_id, nmos::fields::nc::runtime_property_constraints, U("NcPropertyConstraints"), true, true, true, false, value::null()); BST_REQUIRE_EQUAL(property_runtime_property_constraints, property_runtime_property_constraints_); const auto method_get = value_of({ @@ -174,8 +174,8 @@ BST_TEST_CASE(testNcClassDescriptor) { auto parameters = value::array(); - web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - const auto method_get_ = nmos::nc::details::make_nc_method_descriptor(U("Get property value"), nmos::nc_object_get_method_id, U("Get"), U("NcMethodResultPropertyValue"), parameters, false); + web::json::push_back(parameters, nmos::nc::details::make_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + const auto method_get_ = nmos::nc::details::make_method_descriptor(U("Get property value"), nmos::nc_object_get_method_id, U("Get"), U("NcMethodResultPropertyValue"), parameters, false); BST_REQUIRE_EQUAL(method_get, method_get_); } @@ -211,9 +211,9 @@ BST_TEST_CASE(testNcClassDescriptor) { auto parameters = value::array(); - web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); - const auto method_set_ = nmos::nc::details::make_nc_method_descriptor(U("Set property value"), nmos::nc_object_set_method_id, U("Set"), U("NcMethodResult"), parameters, false); + web::json::push_back(parameters, nmos::nc::details::make_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, nmos::nc::details::make_parameter_descriptor(U("Property value"), nmos::fields::nc::value, true, false, value::null())); + const auto method_set_ = nmos::nc::details::make_method_descriptor(U("Set property value"), nmos::nc_object_set_method_id, U("Set"), U("NcMethodResult"), parameters, false); BST_REQUIRE_EQUAL(method_set, method_set_); } @@ -249,9 +249,9 @@ BST_TEST_CASE(testNcClassDescriptor) { auto parameters = value::array(); - web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); - const auto method_get_sequence_item_ = nmos::nc::details::make_nc_method_descriptor(U("Get sequence item"), nmos::nc_object_get_sequence_item_method_id, U("GetSequenceItem"), U("NcMethodResultPropertyValue"), parameters, false); + web::json::push_back(parameters, nmos::nc::details::make_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, nmos::nc::details::make_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + const auto method_get_sequence_item_ = nmos::nc::details::make_method_descriptor(U("Get sequence item"), nmos::nc_object_get_sequence_item_method_id, U("GetSequenceItem"), U("NcMethodResultPropertyValue"), parameters, false); BST_REQUIRE_EQUAL(method_get_sequence_item, method_get_sequence_item_); } @@ -295,10 +295,10 @@ BST_TEST_CASE(testNcClassDescriptor) { auto parameters = value::array(); - web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); - web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); - const auto method_set_sequence_item_ = nmos::nc::details::make_nc_method_descriptor(U("Set sequence item value"), nmos::nc_object_set_sequence_item_method_id, U("SetSequenceItem"), U("NcMethodResult"), parameters, false); + web::json::push_back(parameters, nmos::nc::details::make_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, nmos::nc::details::make_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + web::json::push_back(parameters, nmos::nc::details::make_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); + const auto method_set_sequence_item_ = nmos::nc::details::make_method_descriptor(U("Set sequence item value"), nmos::nc_object_set_sequence_item_method_id, U("SetSequenceItem"), U("NcMethodResult"), parameters, false); BST_REQUIRE_EQUAL(method_set_sequence_item, method_set_sequence_item_); } @@ -334,9 +334,9 @@ BST_TEST_CASE(testNcClassDescriptor) { auto parameters = value::array(); - web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); - const auto method_add_sequence_item_ = nmos::nc::details::make_nc_method_descriptor(U("Add item to sequence"), nmos::nc_object_add_sequence_item_method_id, U("AddSequenceItem"), U("NcMethodResultId"), parameters, false); + web::json::push_back(parameters, nmos::nc::details::make_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, nmos::nc::details::make_parameter_descriptor(U("Value"), nmos::fields::nc::value, true, false, value::null())); + const auto method_add_sequence_item_ = nmos::nc::details::make_method_descriptor(U("Add item to sequence"), nmos::nc_object_add_sequence_item_method_id, U("AddSequenceItem"), U("NcMethodResultId"), parameters, false); BST_REQUIRE_EQUAL(method_add_sequence_item, method_add_sequence_item_); } @@ -372,9 +372,9 @@ BST_TEST_CASE(testNcClassDescriptor) { auto parameters = value::array(); - web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); - const auto method_remove_sequence_item_ = nmos::nc::details::make_nc_method_descriptor(U("Delete sequence item"), nmos::nc_object_remove_sequence_item_method_id, U("RemoveSequenceItem"), U("NcMethodResult"), parameters, false); + web::json::push_back(parameters, nmos::nc::details::make_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + web::json::push_back(parameters, nmos::nc::details::make_parameter_descriptor(U("Index of item in the sequence"), nmos::fields::nc::index, U("NcId"), false, false, value::null())); + const auto method_remove_sequence_item_ = nmos::nc::details::make_method_descriptor(U("Delete sequence item"), nmos::nc_object_remove_sequence_item_method_id, U("RemoveSequenceItem"), U("NcMethodResult"), parameters, false); BST_REQUIRE_EQUAL(method_remove_sequence_item, method_remove_sequence_item_); } @@ -402,8 +402,8 @@ BST_TEST_CASE(testNcClassDescriptor) { auto parameters = value::array(); - web::json::push_back(parameters, nmos::nc::details::make_nc_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); - const auto method_get_sequence_length_ = nmos::nc::details::make_nc_method_descriptor(U("Get sequence length"), nmos::nc_object_get_sequence_length_method_id, U("GetSequenceLength"), U("NcMethodResultLength"), parameters, false); + web::json::push_back(parameters, nmos::nc::details::make_parameter_descriptor(U("Property id"), nmos::fields::nc::id, U("NcPropertyId"), false, false, value::null())); + const auto method_get_sequence_length_ = nmos::nc::details::make_method_descriptor(U("Get sequence length"), nmos::nc_object_get_sequence_length_method_id, U("GetSequenceLength"), U("NcMethodResultLength"), parameters, false); BST_REQUIRE_EQUAL(method_get_sequence_length, method_get_sequence_length_); } @@ -419,7 +419,7 @@ BST_TEST_CASE(testNcClassDescriptor) { U("isDeprecated"), false } }); - const auto event_property_changed_ = nmos::nc::details::make_nc_event_descriptor(U("Property changed event"), nmos::nc_object_property_changed_event_id, U("PropertyChanged"), U("NcPropertyChangedEventData"), false); + const auto event_property_changed_ = nmos::nc::details::make_event_descriptor(U("Property changed event"), nmos::nc_object_property_changed_event_id, U("PropertyChanged"), U("NcPropertyChangedEventData"), false); BST_REQUIRE_EQUAL(event_property_changed, event_property_changed_); const auto nc_object_class = value_of({ @@ -452,7 +452,7 @@ BST_TEST_CASE(testNcClassDescriptor) event_property_changed }) } }); - const auto nc_object_class_ = nmos::nc::details::make_nc_class_descriptor(U("NcObject class descriptor"), nmos::nc_object_class_id, U("NcObject"), nmos::nc::make_nc_object_properties(), nmos::nc::make_nc_object_methods(), nmos::nc::make_nc_object_events()); + const auto nc_object_class_ = nmos::nc::details::make_class_descriptor(U("NcObject class descriptor"), nmos::nc_object_class_id, U("NcObject"), nmos::nc::make_object_properties(), nmos::nc::make_object_methods(), nmos::nc::make_object_events()); BST_REQUIRE_EQUAL(nc_object_class, nc_object_class_); } @@ -522,13 +522,13 @@ BST_TEST_CASE(testNcDatatypeDescriptorStruct) }); auto fields = value::array(); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Role of member in its containing block"), nmos::fields::nc::role, U("NcString"), false, false, value::null())); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("OID of member"), nmos::fields::nc::oid, U("NcOid"), false, false, value::null())); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("TRUE iff member's OID is hardwired into device"), nmos::fields::nc::constant_oid, U("NcBoolean"), false, false, value::null())); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Class ID"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("User label"), nmos::fields::nc::user_label, U("NcString"), true, false, value::null())); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Containing block's OID"), nmos::fields::nc::owner, U("NcOid"), false, false, value::null())); - const auto nc_datatype_descriptor_ = nmos::nc::details::make_nc_datatype_descriptor_struct(U("Descriptor which is specific to a block member"), U("NcBlockMemberDescriptor"), fields, U("NcDescriptor"), value::null()); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Role of member in its containing block"), nmos::fields::nc::role, U("NcString"), false, false, value::null())); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("OID of member"), nmos::fields::nc::oid, U("NcOid"), false, false, value::null())); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("TRUE iff member's OID is hardwired into device"), nmos::fields::nc::constant_oid, U("NcBoolean"), false, false, value::null())); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Class ID"), nmos::fields::nc::class_id, U("NcClassId"), false, false, value::null())); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("User label"), nmos::fields::nc::user_label, U("NcString"), true, false, value::null())); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Containing block's OID"), nmos::fields::nc::owner, U("NcOid"), false, false, value::null())); + const auto nc_datatype_descriptor_ = nmos::nc::details::make_datatype_descriptor_struct(U("Descriptor which is specific to a block member"), U("NcBlockMemberDescriptor"), fields, U("NcDescriptor"), value::null()); BST_REQUIRE_EQUAL(nc_datatype_descriptor, nc_datatype_descriptor_); } @@ -548,7 +548,7 @@ BST_TEST_CASE(testNcDatatypeTypedef) { U("isSequence"), true }, { U("constraints"), value::null() } }); - const auto nc_class_id_ = nmos::nc::details::make_nc_datatype_typedef(U("Sequence of class ID fields"), U("NcClassId"), true, U("NcInt32"), value::null()); + const auto nc_class_id_ = nmos::nc::details::make_datatype_typedef(U("Sequence of class ID fields"), U("NcClassId"), true, U("NcInt32"), value::null()); BST_REQUIRE_EQUAL(nc_class_id, nc_class_id_); } @@ -600,13 +600,13 @@ BST_TEST_CASE(testNcDatatypeDescriptorEnum) }); auto items = value::array(); - web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Unknown"), U("Unknown"), 0)); - web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Normal operation"), U("NormalOperation"), 1)); - web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Device is initializing"), U("Initializing"), 2)); - web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Device is performing a software or firmware update"), U("Updating"), 3)); - web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Device is experiencing a licensing error"), U("LicensingError"), 4)); - web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("Device is experiencing an internal error"), U("InternalError"), 5)); - const auto nc_device_generic_state_ = nmos::nc::details::make_nc_datatype_descriptor_enum(U("Device generic operational state"), U("NcDeviceGenericState"), items, value::null()); + web::json::push_back(items, nmos::nc::details::make_enum_item_descriptor(U("Unknown"), U("Unknown"), 0)); + web::json::push_back(items, nmos::nc::details::make_enum_item_descriptor(U("Normal operation"), U("NormalOperation"), 1)); + web::json::push_back(items, nmos::nc::details::make_enum_item_descriptor(U("Device is initializing"), U("Initializing"), 2)); + web::json::push_back(items, nmos::nc::details::make_enum_item_descriptor(U("Device is performing a software or firmware update"), U("Updating"), 3)); + web::json::push_back(items, nmos::nc::details::make_enum_item_descriptor(U("Device is experiencing a licensing error"), U("LicensingError"), 4)); + web::json::push_back(items, nmos::nc::details::make_enum_item_descriptor(U("Device is experiencing an internal error"), U("InternalError"), 5)); + const auto nc_device_generic_state_ = nmos::nc::details::make_datatype_descriptor_enum(U("Device generic operational state"), U("NcDeviceGenericState"), items, value::null()); BST_REQUIRE_EQUAL(nc_device_generic_state, nc_device_generic_state_); } @@ -623,7 +623,7 @@ BST_TEST_CASE(testNcDatatypeDescriptorPrimitive) { U("constraints"), value::null() } }); - const auto test_primitive_ = nmos::nc::details::make_nc_datatype_descriptor_primitive(U("Primitive datatype descriptor"), U("test_primitive"), value::null()); + const auto test_primitive_ = nmos::nc::details::make_datatype_descriptor_primitive(U("Primitive datatype descriptor"), U("test_primitive"), value::null()); BST_REQUIRE_EQUAL(test_primitive, test_primitive_); } @@ -713,8 +713,8 @@ BST_TEST_CASE(testConstraints) // constraints // runtime constraints - const auto runtime_property_string_constraints = nmos::nc::details::make_nc_property_constraints_string(property_string_id, 10, U("^[0-9]+$")); - const auto runtime_property_int32_constraints = nmos::nc::details::make_nc_property_constraints_number(property_int32_id, 10, 1000, 1); + const auto runtime_property_string_constraints = nmos::nc::details::make_property_constraints_string(property_string_id, 10, U("^[0-9]+$")); + const auto runtime_property_int32_constraints = nmos::nc::details::make_property_constraints_number(property_int32_id, 10, 1000, 1); const auto runtime_property_constraints = value_of({ { runtime_property_string_constraints }, @@ -722,65 +722,65 @@ BST_TEST_CASE(testConstraints) }); // property constraints - const auto property_string_constraints = nmos::nc::details::make_nc_parameter_constraints_string(5, U("^[a-z]+$")); - const auto property_int32_constraints = nmos::nc::details::make_nc_parameter_constraints_number(50, 500, 5); + const auto property_string_constraints = nmos::nc::details::make_parameter_constraints_string(5, U("^[a-z]+$")); + const auto property_int32_constraints = nmos::nc::details::make_parameter_constraints_number(50, 500, 5); // datatype constraints - const auto datatype_string_constraints = nmos::nc::details::make_nc_parameter_constraints_string(2, U("^[0-9a-z]+$")); - const auto datatype_int32_constraints = nmos::nc::details::make_nc_parameter_constraints_number(100, 250, 10); + const auto datatype_string_constraints = nmos::nc::details::make_parameter_constraints_string(2, U("^[0-9a-z]+$")); + const auto datatype_int32_constraints = nmos::nc::details::make_parameter_constraints_number(100, 250, 10); // datatypes - const auto no_constraints_bool_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints boolean datatype"), U("NoConstraintsBoolean"), false, U("NcBoolean"), value::null()); - const auto no_constraints_int16_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints int16 datatype"), U("NoConstraintsInt16"), false, U("NcInt16"), value::null()); - const auto no_constraints_int32_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints int32 datatype"), U("NoConstraintsInt32"), false, U("NcInt32"), value::null()); - const auto no_constraints_int64_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints int64 datatype"), U("NoConstraintsInt64"), false, U("NcInt64"), value::null()); - const auto no_constraints_uint16_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints uint16 datatype"), U("NoConstraintsUint16"), false, U("NcUint16"), value::null()); - const auto no_constraints_uint32_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints uint32 datatype"), U("NoConstraintsUint32"), false, U("NcUint32"), value::null()); - const auto no_constraints_uint64_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints uint64 datatype"), U("NoConstraintsUint64"), false, U("NcUint64"), value::null()); - const auto no_constraints_float32_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints float32 datatype"), U("NoConstraintsFloat32"), false, U("NcFloat32"), value::null()); - const auto no_constraints_float64_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints float64 datatype"), U("NoConstraintsFloat64"), false, U("NcFloat64"), value::null()); - const auto no_constraints_string_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints string datatype"), U("NoConstraintsString"), false, U("NcString"), value::null()); - const auto with_constraints_string_datatype = nmos::nc::details::make_nc_datatype_typedef(U("With constraints string datatype"), U("WithConstraintsString"), false, U("NcString"), datatype_string_constraints); - const auto with_constraints_int32_datatype = nmos::nc::details::make_nc_datatype_typedef(U("With constraints int32 datatype"), U("WithConstraintsInt32"), false, U("NcInt32"), datatype_int32_constraints); - const auto no_constraints_int32_seq_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints int64 datatype"), U("NoConstraintsInt64"), true, U("NcInt32"), value::null()); - const auto no_constraints_string_seq_datatype = nmos::nc::details::make_nc_datatype_typedef(U("No constraints string datatype"), U("NoConstraintsString"), true, U("NcString"), value::null()); + const auto no_constraints_bool_datatype = nmos::nc::details::make_datatype_typedef(U("No constraints boolean datatype"), U("NoConstraintsBoolean"), false, U("NcBoolean"), value::null()); + const auto no_constraints_int16_datatype = nmos::nc::details::make_datatype_typedef(U("No constraints int16 datatype"), U("NoConstraintsInt16"), false, U("NcInt16"), value::null()); + const auto no_constraints_int32_datatype = nmos::nc::details::make_datatype_typedef(U("No constraints int32 datatype"), U("NoConstraintsInt32"), false, U("NcInt32"), value::null()); + const auto no_constraints_int64_datatype = nmos::nc::details::make_datatype_typedef(U("No constraints int64 datatype"), U("NoConstraintsInt64"), false, U("NcInt64"), value::null()); + const auto no_constraints_uint16_datatype = nmos::nc::details::make_datatype_typedef(U("No constraints uint16 datatype"), U("NoConstraintsUint16"), false, U("NcUint16"), value::null()); + const auto no_constraints_uint32_datatype = nmos::nc::details::make_datatype_typedef(U("No constraints uint32 datatype"), U("NoConstraintsUint32"), false, U("NcUint32"), value::null()); + const auto no_constraints_uint64_datatype = nmos::nc::details::make_datatype_typedef(U("No constraints uint64 datatype"), U("NoConstraintsUint64"), false, U("NcUint64"), value::null()); + const auto no_constraints_float32_datatype = nmos::nc::details::make_datatype_typedef(U("No constraints float32 datatype"), U("NoConstraintsFloat32"), false, U("NcFloat32"), value::null()); + const auto no_constraints_float64_datatype = nmos::nc::details::make_datatype_typedef(U("No constraints float64 datatype"), U("NoConstraintsFloat64"), false, U("NcFloat64"), value::null()); + const auto no_constraints_string_datatype = nmos::nc::details::make_datatype_typedef(U("No constraints string datatype"), U("NoConstraintsString"), false, U("NcString"), value::null()); + const auto with_constraints_string_datatype = nmos::nc::details::make_datatype_typedef(U("With constraints string datatype"), U("WithConstraintsString"), false, U("NcString"), datatype_string_constraints); + const auto with_constraints_int32_datatype = nmos::nc::details::make_datatype_typedef(U("With constraints int32 datatype"), U("WithConstraintsInt32"), false, U("NcInt32"), datatype_int32_constraints); + const auto no_constraints_int32_seq_datatype = nmos::nc::details::make_datatype_typedef(U("No constraints int64 datatype"), U("NoConstraintsInt64"), true, U("NcInt32"), value::null()); + const auto no_constraints_string_seq_datatype = nmos::nc::details::make_datatype_typedef(U("No constraints string datatype"), U("NoConstraintsString"), true, U("NcString"), value::null()); enum enum_value { foo, bar, baz }; auto items = value::array(); - web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("foo"), U("foo"), enum_value::foo)); - web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("bar"), U("bar"), enum_value::bar)); - web::json::push_back(items, nmos::nc::details::make_nc_enum_item_descriptor(U("baz"), U("baz"), enum_value::baz)); - const auto enum_datatype = nmos::nc::details::make_nc_datatype_descriptor_enum(U("enum datatype"), U("enumDatatype"), items, value::null()); // no datatype constraints for enum datatype + web::json::push_back(items, nmos::nc::details::make_enum_item_descriptor(U("foo"), U("foo"), enum_value::foo)); + web::json::push_back(items, nmos::nc::details::make_enum_item_descriptor(U("bar"), U("bar"), enum_value::bar)); + web::json::push_back(items, nmos::nc::details::make_enum_item_descriptor(U("baz"), U("baz"), enum_value::baz)); + const auto enum_datatype = nmos::nc::details::make_datatype_descriptor_enum(U("enum datatype"), U("enumDatatype"), items, value::null()); // no datatype constraints for enum datatype auto simple_struct_fields = value::array(); - web::json::push_back(simple_struct_fields, nmos::nc::details::make_nc_field_descriptor(U("simple enum property example"), U("simpleEnumProperty"), U("enumDatatype"), false, false, value::null())); // no field constraints for enum field, as it is already described by its type - web::json::push_back(simple_struct_fields, nmos::nc::details::make_nc_field_descriptor(U("simple string property example"), U("simpleStringProperty"), U("NcString"), false, false, datatype_string_constraints)); - web::json::push_back(simple_struct_fields, nmos::nc::details::make_nc_field_descriptor(U("simple number property example"), U("simpleNumberProperty"), U("NcInt32"), false, false, datatype_int32_constraints)); - web::json::push_back(simple_struct_fields, nmos::nc::details::make_nc_field_descriptor(U("simle boolean property example"), U("simpleBooleanProperty"), U("NcBoolean"), false, false, value::null())); // no field constraints for boolean field, as it is already described by its type - const auto simple_struct_datatype = nmos::nc::details::make_nc_datatype_descriptor_struct(U("simple struct datatype"), U("simpleStructDatatype"), simple_struct_fields, value::null()); // no datatype constraints for struct datatype + web::json::push_back(simple_struct_fields, nmos::nc::details::make_field_descriptor(U("simple enum property example"), U("simpleEnumProperty"), U("enumDatatype"), false, false, value::null())); // no field constraints for enum field, as it is already described by its type + web::json::push_back(simple_struct_fields, nmos::nc::details::make_field_descriptor(U("simple string property example"), U("simpleStringProperty"), U("NcString"), false, false, datatype_string_constraints)); + web::json::push_back(simple_struct_fields, nmos::nc::details::make_field_descriptor(U("simple number property example"), U("simpleNumberProperty"), U("NcInt32"), false, false, datatype_int32_constraints)); + web::json::push_back(simple_struct_fields, nmos::nc::details::make_field_descriptor(U("simle boolean property example"), U("simpleBooleanProperty"), U("NcBoolean"), false, false, value::null())); // no field constraints for boolean field, as it is already described by its type + const auto simple_struct_datatype = nmos::nc::details::make_datatype_descriptor_struct(U("simple struct datatype"), U("simpleStructDatatype"), simple_struct_fields, value::null()); // no datatype constraints for struct datatype auto fields = value::array(); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Enum property example"), U("enumProperty"), U("enumDatatype"), false, false, value::null())); // no field constraints for enum field, as it is already described by its type - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("String property example"), U("stringProperty"), U("NcString"), false, false, datatype_string_constraints)); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Number property example"), U("numberProperty"), U("NcInt32"), false, false, datatype_int32_constraints)); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Boolean property example"), U("booleanProperty"), U("NcBoolean"), false, false, value::null())); // no field constraints for boolean field, as it is already described by its type - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Struct property example"), U("structProperty"), U("simpleStructDatatype"), false, false, value::null())); // no datatype constraints for struct datatype - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Sequence enum property example"), U("sequenceEnumProperty"), U("enumDatatype"), false, true, value::null())); // no field constraints for enum field, as it is already described by its type - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Sequence string property example"), U("sequenceStringProperty"), U("NcString"), false, true, datatype_string_constraints)); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Sequence number property example"), U("sequenceNumberProperty"), U("NcInt32"), false, true, datatype_int32_constraints)); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Sequence boolean property example"), U("sequenceBooleanProperty"), U("NcBoolean"), false, true, value::null())); // no field constraints for boolean field, as it is already described by its type - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Sequence struct property example"), U("sequenceStructProperty"), U("simpleStructDatatype"), false, true, value::null())); // no field constraints for struct field - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Enum property example"), U("enumPropertyNullable"), U("enumDatatype"), true, false, value::null())); // no field constraints for enum field, as it is already described by its type - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable String property example"), U("stringPropertyNullable"), U("NcString"), true, false, datatype_string_constraints)); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Number property example"), U("numberPropertyNullable"), U("NcInt32"), true, false, datatype_int32_constraints)); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Boolean property example"), U("booleanPropertyNullable"), U("NcBoolean"), true, false, value::null())); // no field constraints for boolean field, as it is already described by its type - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Struct property example"), U("structPropertyNullable"), U("simpleStructDatatype"), true, false, value::null())); // no datatype constraints for struct datatype - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Sequence enum property example"), U("sequenceEnumPropertyNullable"), U("enumDatatype"), true, true, value::null())); // no field constraints for enum field, as it is already described by its type - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Sequence string property example"), U("sequenceStringPropertyNullable"), U("NcString"), true, true, datatype_string_constraints)); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Sequence number property example"), U("sequenceNumberPropertyNullable"), U("NcInt32"), true, true, datatype_int32_constraints)); - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Sequence boolean property example"), U("sequenceBooleanPropertyNullable"), U("NcBoolean"), true, true, value::null())); // no field constraints for boolean field, as it is already described by its type - web::json::push_back(fields, nmos::nc::details::make_nc_field_descriptor(U("Nullable Sequence struct property example"), U("sequenceStructPropertyNullable"), U("simpleStructDatatype"), true, true, value::null())); // no field constraints for struct field - const auto struct_datatype = nmos::nc::details::make_nc_datatype_descriptor_struct(U("struct datatype"), U("structDatatype"), fields, value::null()); // no datatype constraints for struct datatype + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Enum property example"), U("enumProperty"), U("enumDatatype"), false, false, value::null())); // no field constraints for enum field, as it is already described by its type + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("String property example"), U("stringProperty"), U("NcString"), false, false, datatype_string_constraints)); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Number property example"), U("numberProperty"), U("NcInt32"), false, false, datatype_int32_constraints)); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Boolean property example"), U("booleanProperty"), U("NcBoolean"), false, false, value::null())); // no field constraints for boolean field, as it is already described by its type + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Struct property example"), U("structProperty"), U("simpleStructDatatype"), false, false, value::null())); // no datatype constraints for struct datatype + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Sequence enum property example"), U("sequenceEnumProperty"), U("enumDatatype"), false, true, value::null())); // no field constraints for enum field, as it is already described by its type + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Sequence string property example"), U("sequenceStringProperty"), U("NcString"), false, true, datatype_string_constraints)); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Sequence number property example"), U("sequenceNumberProperty"), U("NcInt32"), false, true, datatype_int32_constraints)); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Sequence boolean property example"), U("sequenceBooleanProperty"), U("NcBoolean"), false, true, value::null())); // no field constraints for boolean field, as it is already described by its type + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Sequence struct property example"), U("sequenceStructProperty"), U("simpleStructDatatype"), false, true, value::null())); // no field constraints for struct field + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Nullable Enum property example"), U("enumPropertyNullable"), U("enumDatatype"), true, false, value::null())); // no field constraints for enum field, as it is already described by its type + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Nullable String property example"), U("stringPropertyNullable"), U("NcString"), true, false, datatype_string_constraints)); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Nullable Number property example"), U("numberPropertyNullable"), U("NcInt32"), true, false, datatype_int32_constraints)); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Nullable Boolean property example"), U("booleanPropertyNullable"), U("NcBoolean"), true, false, value::null())); // no field constraints for boolean field, as it is already described by its type + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Nullable Struct property example"), U("structPropertyNullable"), U("simpleStructDatatype"), true, false, value::null())); // no datatype constraints for struct datatype + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Nullable Sequence enum property example"), U("sequenceEnumPropertyNullable"), U("enumDatatype"), true, true, value::null())); // no field constraints for enum field, as it is already described by its type + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Nullable Sequence string property example"), U("sequenceStringPropertyNullable"), U("NcString"), true, true, datatype_string_constraints)); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Nullable Sequence number property example"), U("sequenceNumberPropertyNullable"), U("NcInt32"), true, true, datatype_int32_constraints)); + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Nullable Sequence boolean property example"), U("sequenceBooleanPropertyNullable"), U("NcBoolean"), true, true, value::null())); // no field constraints for boolean field, as it is already described by its type + web::json::push_back(fields, nmos::nc::details::make_field_descriptor(U("Nullable Sequence struct property example"), U("sequenceStructPropertyNullable"), U("simpleStructDatatype"), true, true, value::null())); // no field constraints for struct field + const auto struct_datatype = nmos::nc::details::make_datatype_descriptor_struct(U("struct datatype"), U("structDatatype"), fields, value::null()); // no datatype constraints for struct datatype // setup datatypes in control_protocol_state nmos::experimental::control_protocol_state control_protocol_state; diff --git a/Development/nmos/test/control_protocol_utils_test.cpp b/Development/nmos/test/control_protocol_utils_test.cpp index ff9938095..ffdf7694b 100644 --- a/Development/nmos/test/control_protocol_utils_test.cpp +++ b/Development/nmos/test/control_protocol_utils_test.cpp @@ -328,7 +328,7 @@ BST_TEST_CASE(testActivateDeactivateReceiverMonitor) // check that the property changed handler gets called reset_monitor_called = true; - return nmos::nc::details::make_nc_method_result({ nmos::nc_method_status::ok }); + return nmos::nc::details::make_method_result({ nmos::nc_method_status::ok }); }; nmos::experimental::control_protocol_state control_protocol_state(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, reset_monitor); @@ -690,7 +690,7 @@ BST_TEST_CASE(testActivateDeactivateSenderMonitor) // check that the property changed handler gets called reset_monitor_called = true; - return nmos::nc::details::make_nc_method_result({ nmos::nc_method_status::ok }); + return nmos::nc::details::make_method_result({ nmos::nc_method_status::ok }); }; nmos::experimental::control_protocol_state control_protocol_state(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, reset_monitor); @@ -830,7 +830,7 @@ BST_TEST_CASE(testSetMonitorStatusWithDelay) // check that the property changed handler gets called reset_monitor_called = true; - return nmos::nc::details::make_nc_method_result({nmos::nc_method_status::ok}); + return nmos::nc::details::make_method_result({nmos::nc_method_status::ok}); }; nmos::monitor_status_pending_handler monitor_status_pending = [&]() @@ -1148,9 +1148,9 @@ BST_TEST_CASE(testFindTouchpointResources) // Create Device Model auto oid = nmos::root_block_oid; - auto monitor1 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint1_id})} })); - auto monitor2 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint2_id})} })); - auto monitor3 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon3"), U("monitor 3"), U("monitor 3"), value_of({ {nmos::nc::details::make_nc_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, non_existant_id})} })); + auto monitor1 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon1"), U("monitor 1"), U("monitor 1"), value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint1_id})} })); + auto monitor2 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon2"), U("monitor 2"), U("monitor 2"), value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, touchpoint2_id})} })); + auto monitor3 = nmos::make_receiver_monitor(++oid, true, nmos::root_block_oid, U("mon3"), U("monitor 3"), U("monitor 3"), value_of({ {nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, non_existant_id})} })); nmos::resources resources; // Insert dummy NMOS resources From d450ba46fea20c805be29a3e796ebae1ca56c8c9 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 12 Sep 2025 15:39:04 +0100 Subject: [PATCH 245/250] Use set_control_protocol_property for setting example temperature sensor control protocol object --- .../nmos-cpp-node/node_implementation.cpp | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index c27b0c037..e0349f680 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1370,6 +1370,8 @@ void node_implementation_run(nmos::node_model& model, nmos::experimental::contro auto& control_protocol_resources = model.control_protocol_resources; auto get_control_protocol_property = nmos::make_get_control_protocol_property_handler(control_protocol_resources, control_protocol_state, gate); + auto set_control_protocol_property = nmos::make_set_control_protocol_property_handler(control_protocol_resources, control_protocol_state, gate); + auto set_receiver_monitor_link_status = nmos::make_set_receiver_monitor_link_status_handler(control_protocol_resources, control_protocol_state, gate); auto set_receiver_monitor_connection_status = nmos::make_set_receiver_monitor_connection_status_handler(control_protocol_resources, control_protocol_state, gate); auto set_receiver_monitor_external_synchronization_status = nmos::make_set_receiver_monitor_external_synchronization_status_handler(control_protocol_resources, control_protocol_state, gate); @@ -1390,10 +1392,10 @@ void node_implementation_run(nmos::node_model& model, nmos::experimental::contro auto cancellation_source = pplx::cancellation_token_source(); auto token = cancellation_source.get_token(); - auto events = pplx::do_while([&model, seed_id, how_many, simulate_status_monitor_activity, ws_sender_ports, rtp_receiver_ports, rtp_sender_ports, get_control_protocol_property, set_receiver_monitor_link_status, set_receiver_monitor_connection_status, set_receiver_monitor_external_synchronization_status, set_receiver_monitor_stream_status, set_receiver_monitor_synchronization_source_id, set_sender_monitor_link_status, set_sender_monitor_transmission_status, set_sender_monitor_external_synchronization_status, set_sender_monitor_essence_status, set_sender_monitor_synchronization_source_id, events_engine, &gate, token] + auto events = pplx::do_while([&model, seed_id, how_many, simulate_status_monitor_activity, ws_sender_ports, rtp_receiver_ports, rtp_sender_ports, get_control_protocol_property, set_receiver_monitor_link_status, set_receiver_monitor_connection_status, set_receiver_monitor_external_synchronization_status, set_receiver_monitor_stream_status, set_receiver_monitor_synchronization_source_id, set_sender_monitor_link_status, set_sender_monitor_transmission_status, set_sender_monitor_external_synchronization_status, set_sender_monitor_essence_status, set_sender_monitor_synchronization_source_id, set_control_protocol_property, events_engine, &gate, token] { const auto event_interval = std::uniform_real_distribution<>(0.5, 5.0)(*events_engine); - return pplx::complete_after(std::chrono::milliseconds(std::chrono::milliseconds::rep(1000 * event_interval)), token).then([&model, seed_id, how_many, simulate_status_monitor_activity, ws_sender_ports, rtp_receiver_ports, rtp_sender_ports, get_control_protocol_property, set_receiver_monitor_link_status, set_receiver_monitor_connection_status, set_receiver_monitor_external_synchronization_status, set_receiver_monitor_stream_status, set_receiver_monitor_synchronization_source_id, set_sender_monitor_link_status, set_sender_monitor_transmission_status, set_sender_monitor_external_synchronization_status, set_sender_monitor_essence_status, set_sender_monitor_synchronization_source_id, events_engine, &gate] + return pplx::complete_after(std::chrono::milliseconds(std::chrono::milliseconds::rep(1000 * event_interval)), token).then([&model, seed_id, how_many, simulate_status_monitor_activity, ws_sender_ports, rtp_receiver_ports, rtp_sender_ports, get_control_protocol_property, set_receiver_monitor_link_status, set_receiver_monitor_connection_status, set_receiver_monitor_external_synchronization_status, set_receiver_monitor_stream_status, set_receiver_monitor_synchronization_source_id, set_sender_monitor_link_status, set_sender_monitor_transmission_status, set_sender_monitor_external_synchronization_status, set_sender_monitor_essence_status, set_sender_monitor_synchronization_source_id, set_control_protocol_property, events_engine, &gate] { auto lock = model.write_lock(); @@ -1437,7 +1439,7 @@ void node_implementation_run(nmos::node_model& model, nmos::experimental::contro // update temperature sensor { const auto temperature_sensor_control_class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 3 }); // hmm, maybe pull out temperature_sensor_control_class_id to impl namespace - const web::json::field_as_number temperature{ U("temperature") }; // hmm, maybe pull out temperature field to impl namespace + const auto temperature_value_property_id = nmos::nc_property_id({3, 1}); auto& resources = model.control_protocol_resources; @@ -1448,16 +1450,7 @@ void node_implementation_run(nmos::node_model& model, nmos::experimental::contro if (resources.end() != found) { - const auto property_changed_event = nmos::nc::make_property_changed_event(nmos::fields::nc::oid(found->data), - { - { {3, 1}, nmos::nc_property_change_type::type::value_changed, web::json::value(temp.scaled_value()) } // hmm, maybe pull out {3, 1} temperature property id to impl namespace - }); - - nmos::nc::modify_resource(model.control_protocol_resources, found->id, [&](nmos::resource& resource) - { - resource.data[temperature] = temp.scaled_value(); - - }, property_changed_event); + set_control_protocol_property(nmos::fields::nc::oid(found->data), temperature_value_property_id, web::json::value(temp.scaled_value())); } } From 0e266002f224fc4b2f144174fc7ebee4121db6d6 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 12 Sep 2025 15:54:09 +0100 Subject: [PATCH 246/250] Remove vs files --- .vs/CMakeWorkspaceSettings.json | 3 --- .vs/ProjectSettings.json | 3 --- .vs/VSWorkspaceState.json | 7 ------- .vs/nmos-cpp/v17/workspaceFileList.bin | Bin 246154 -> 0 bytes .vs/slnx.sqlite | Bin 6590464 -> 0 bytes 5 files changed, 13 deletions(-) delete mode 100644 .vs/CMakeWorkspaceSettings.json delete mode 100644 .vs/ProjectSettings.json delete mode 100644 .vs/VSWorkspaceState.json delete mode 100644 .vs/nmos-cpp/v17/workspaceFileList.bin delete mode 100644 .vs/slnx.sqlite diff --git a/.vs/CMakeWorkspaceSettings.json b/.vs/CMakeWorkspaceSettings.json deleted file mode 100644 index d3e1057f4..000000000 --- a/.vs/CMakeWorkspaceSettings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "enableCMake": false -} \ No newline at end of file diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json deleted file mode 100644 index 0cf5ea503..000000000 --- a/.vs/ProjectSettings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "CurrentProjectSetting": "No Configurations" -} \ No newline at end of file diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json deleted file mode 100644 index 7586180bb..000000000 --- a/.vs/VSWorkspaceState.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "ExpandedNodes": [ - "" - ], - "SelectedNode": "\\nmos-cpp.sln", - "PreviewInSolutionExplorer": false -} \ No newline at end of file diff --git a/.vs/nmos-cpp/v17/workspaceFileList.bin b/.vs/nmos-cpp/v17/workspaceFileList.bin deleted file mode 100644 index e238e1ec9872507c3b0b19051e2c04a72377149a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246154 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
    Ba?vntqSX5x({&wJg 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 z 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
    vq^f0W16_Yc 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�H=K;aV5f8kEjJlgIdG?J5GLr2Y~DxtwdXP3 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
  1. yeMEycn#tY?W=9kQ%;3yuC7j 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

    +bW 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

    g9cK-%nF4Ad)%mh>z&Pg8tM^l)0rGZ-DW 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 From c04393d092505241ae2e84ebfeb56d764b097e4f Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 12 Sep 2025 15:55:06 +0100 Subject: [PATCH 247/250] Remove test config files --- Development/nmos-cpp-node/config.is10.json | 376 --------------------- Development/nmos-cpp-node/config.jrt.json | 311 ----------------- 2 files changed, 687 deletions(-) delete mode 100644 Development/nmos-cpp-node/config.is10.json delete mode 100644 Development/nmos-cpp-node/config.jrt.json diff --git a/Development/nmos-cpp-node/config.is10.json b/Development/nmos-cpp-node/config.is10.json deleted file mode 100644 index 2af0b410a..000000000 --- a/Development/nmos-cpp-node/config.is10.json +++ /dev/null @@ -1,376 +0,0 @@ -// Note: C++/JavaScript-style single and multi-line comments are permitted and ignored in nmos-cpp config files - -// Configuration settings and defaults -{ - // Custom settings for the example node implementation - - // node_tags, device_tags: used in resource tags fields - // "Each tag has a single key, but MAY have multiple values." - // See https://specs.amwa.tv/is-04/releases/v1.3.2/docs/APIs_-_Common_Keys.html#tags - // { - // "tag_1": [ "tag_1_value_1", "tag_1_value_2" ], - // "tag_2": [ "tag_2_value_1" ] - // } - //"node_tags": {}, - //"device_tags": {}, - - // how_many: provides for very basic testing of a node with many sub-resources of each type - //"how_many": 4, - - // activate_senders: controls whether to activate senders on start up (true, default) or not (false) - //"activate_senders": false, - - // senders, receivers: controls which kinds of sender and receiver are instantiated by the example node - // the values must be an array of unique strings identifying the kinds of 'port', like ["v", "a", "d"], see impl::ports - // when omitted, all ports are instantiated - //"senders": ["v", "a"], - //"receivers": [], - - // frame_rate: controls the grain_rate of video, audio and ancillary data sources and flows - // and the equivalent parameter constraint on video receivers - // the value must be an object like { "numerator": 25, "denominator": 1 } - //"frame_rate": { "numerator": 60000, "denominator": 1001 }, - - // frame_width, frame_height: control the frame_width and frame_height of video flows - //"frame_width": 3840, - //"frame_height": 2160, - - // interlace_mode: controls the interlace_mode of video flows, see nmos::interlace_mode - // when omitted, a default of "progressive" or "interlaced_tff" is used based on the frame_rate, etc. - //"interlace_mode": "progressive", - - // colorspace: controls the colorspace of video flows, see nmos::colorspace - //"colorspace": "BT709", - - // transfer_characteristic: controls the transfer characteristic system of video flows, see nmos::transfer_characteristic - //"transfer_characteristic": "SDR", - - // color_sampling: controls the color (sub-)sampling mode of video flows, see sdp::sampling - //"color_sampling": "YCbCr-4:2:2", - - // component_depth: controls the bits per component sample of video flows - //"component_depth": 10, - - // video_type: media type of video flows, e.g. "video/raw" or "video/jxsv", see nmos::media_types - //"video_type": "video/jxsv", - - // channel_count: controls the number of channels in audio sources - //"channel_count": 8, - - // smpte2022_7: controls whether senders and receivers have one leg (false) or two legs (true, default) - //"smpte2022_7": false, - - // Configuration settings and defaults for logging - - // error_log [registry, node]: filename for the error log or an empty string to write to stderr - //"error_log": "", - - // access_log [registry, node]: filename for the access log (in Common Log Format) or an empty string to discard - //"access_log": "", - - // logging_level [registry, node]: integer value, between 40 (least verbose, only fatal messages) and -40 (most verbose) - //"logging_level": 0, - - // logging_categories [registry, node]: array of logging categories to be included in the error log - //"logging_categories": ["node_implementation"], - - // Configuration settings and defaults for the NMOS APIs - - // host_name [registry, node]: the fully-qualified host name for which to advertise services, also used to construct response headers and fields in the data model - //"host_name": "", // when omitted or an empty string, the default is used - - // domain [registry, node]: the domain on which to browse for services or an empty string to use the default domain (specify "local." to explictly select mDNS) - "domain": "local.", - - // host_address/host_addresses [registry, node]: IP addresses used to construct response headers (e.g. 'Link' or 'Location'), and host and URL fields in the data model - //"host_address": "127.0.0.1", - //"host_addresses": array-of-ip-address-strings, - - // is04_versions [registry, node]: used to specify the enabled API versions (advertised via 'api_ver') for a version-locked configuration - //"is04_versions": ["v1.2", "v1.3"], - - // is05_versions [node]: used to specify the enabled API versions for a version-locked configuration - //"is05_versions": ["v1.0", "v1.1"], - - // is07_versions [node]: used to specify the enabled API versions for a version-locked configuration - //"is07_versions": ["v1.0"], - - // is08_versions [node]: used to specify the enabled API versions for a version-locked configuration - //"is08_versions": ["v1.0"], - - // is09_versions [registry, node]: used to specify the enabled API versions for a version-locked configuration - //"is09_versions": ["v1.0"], - - // is10_versions [registry, node]: used to specify the enabled API versions for a version-locked configuration - //"is10_versions": ["v1.0"], - - // pri [registry, node]: used for the 'pri' TXT record; specifying nmos::service_priorities::no_priority (maximum value) disables advertisement completely - //"pri": 100, - - // highest_pri, lowest_pri [node]: used to specify the (inclusive) range of suitable 'pri' values of discovered Registration and System APIs, to avoid development and live systems colliding - "highest_pri": 0, - "lowest_pri": 60, - - // authorization_highest_pri, authorization_lowest_pri [registry, node]: used to specify the (inclusive) range of suitable 'pri' values of discovered Authorization APIs, to avoid development and live systems colliding - //"authorization_highest_pri": 0, - //"authorization_lowest_pri": 2147483647, - - // discovery_backoff_min/discovery_backoff_max/discovery_backoff_factor [registry, node]: used to back-off after errors interacting with all discoverable service instances - // e.g. Registration APIs, System APIs, Authorization APIs or OCSP servers - //"discovery_backoff_min": 1, - //"discovery_backoff_max": 30, - //"discovery_backoff_factor": 1.5, - - // registry_address [node]: IP address or host name used to construct request URLs for registry APIs (if not discovered via DNS-SD) - //"registry_address": "43.195.121.126", - - // registry_version [node]: used to construct request URLs for registry APIs (if not discovered via DNS-SD) - //"registry_version": "v1.2", - - // port numbers [registry, node]: ports to which clients should connect for each API - - // http_port [registry, node]: if specified, this becomes the default port for each HTTP API and the next higher port becomes the default for each WebSocket API - "http_port": 7000, - - // registration_port [node]: used to construct request URLs for the registry's Registration API (if not discovered via DNS-SD) - //"registration_port": 80, - //"node_port": 3212, - //"connection_port": 3215, - //"events_port": 3216, - //"events_ws_port": 3217, - //"channelmapping_port": 3215, - // system_port [node]: used to construct request URLs for the System API (if not discovered via DNS-SD) - //"system_port": 10641, - - // listen_backlog [registry, node]: the maximum length of the queue of pending connections, or zero for the implementation default (the implementation may not honour this value) - //"listen_backlog": 0, - - // registration_heartbeat_interval [registry, node]: - // "Nodes are expected to peform a heartbeat every 5 seconds by default." - // See https://specs.amwa.tv/is-04/releases/v1.2.0/docs/4.1._Behaviour_-_Registration.html#heartbeating - //"registration_heartbeat_interval": 5, - - // registration_request_max [node]: timeout for interactions with the Registration API /resource endpoint - //"registration_request_max": 30, - - // registration_heartbeat_max [node]: timeout for interactions with the Registration API /health/nodes endpoint - // Note that the default timeout is the same as the default heartbeat interval, in order that there is then a reasonable opportunity to try the next available Registration API - // though in some circumstances registration expiry could potentially still be avoided with a timeout that is (almost) twice the garbage collection interval... - //"registration_heartbeat_max": 5, - - // immediate_activation_max [node]: timeout for immediate activations within the Connection API /staged endpoint - //"immediate_activation_max": 30, - - // events_heartbeat_interval [node, client]: - // "Upon connection, the client is required to report its health every 5 seconds in order to maintain its session and subscription." - // See https://specs.amwa.tv/is-07/releases/v1.0.1/docs/5.2._Transport_-_Websocket.html#41-heartbeats - //"events_heartbeat_interval": 5, - - // events_expiry_interval [node]: - // "The server is expected to check health commands and after a 12 seconds timeout (2 consecutive missed health commands plus 2 seconds to allow for latencies) - // it should clear the subscriptions for that particular client and close the websocket connection." - // See https://specs.amwa.tv/is-07/releases/v1.0.1/docs/5.2._Transport_-_Websocket.html#41-heartbeats - //"events_expiry_interval": 12, - - // system_address [node]: IP address or host name used to construct request URLs for the System API (if not discovered via DNS-SD) - //"system_address": ip-address-string, - - // system_version [node]: used to construct request URLs for the System API (if not discovered via DNS-SD) - //"system_version": "v1.0", - - // system_request_max [node]: timeout for interactions with the System API - //"system_request_max": 30, - - // Configuration settings and defaults for experimental extensions - - // seed id [registry, node]: optional, used to generate repeatable id values when running with the same configuration - //"seed_id": uuid-string, - - // label [registry, node]: used in resource label field - //"label": "", - - // description [registry, node]: used in resource description field - //"description": "", - - // port numbers [registry, node]: ports to which clients should connect for each API - // see http_port - - //"settings_port": 3209, - //"logging_port": 5106, - - // addresses [registry, node]: IP addresses on which to listen for each API, or empty string for the wildcard address - - // server_address [registry, node]: if specified, this becomes the default address on which to listen for each API instead of the wildcard address - //"server_address": "", - - // addresses [registry, node]: IP addresses on which to listen for specific APIs - - //"settings_address": "127.0.0.1", - //"logging_address": "", - - // client_address [registry, node]: IP address of the network interface to bind client connections - // for now, only supporting HTTP/HTTPS client connections on Linux - //"client_address": "", - - // logging_limit [registry, node]: maximum number of log events cached for the Logging API - //"logging_limit": 1234, - - // logging_paging_default/logging_paging_limit [registry, node]: default/maximum number of results per "page" when using the Logging API (a client may request a lower limit) - //"logging_paging_default": 100, - //"logging_paging_limit": 100, - - // http_trace [registry, node]: whether server should enable (default) or disable support for HTTP TRACE - //"http_trace": true, - - // proxy_map [registry, node]: mapping between the port numbers to which the client connects, and the port numbers on which the server should listen, if different - // for use with a reverse proxy; each element of the array is an object like { "client_port": 80, "server_port": 8080 } - //"proxy_map": array-of-mappings, - - // proxy_address [registry, node]: address of the forward proxy to use when making HTTP requests or WebSocket connections, or an empty string for no proxy - //"proxy_address": "127.0.0.1", - - // proxy_port [registry, node]: forward proxy port - //"proxy_port": 8080, - - // discovery_mode [node]: whether the discovered host name (1) or resolved addresses (2) are used to construct request URLs for Registration APIs or System APIs - //"discovery_mode": 1, - - // href_mode [registry, node]: whether the host name (1), addresses (2) or both (3) are used to construct response headers, and host and URL fields in the data model - //"href_mode": 1, - - // client_secure [registry, node]: whether clients should use a secure connection for communication (https and wss) - // when true, CA root certificates must also be configured - "client_secure": true, - - // ca_certificate_file [registry, node]: full path of certification authorities file in PEM format - // on Windows, if C++ REST SDK is built with CPPREST_HTTP_CLIENT_IMPL=winhttp (reported as "client=winhttp" by nmos::get_build_settings_info) - // the trusted root CA certificates must also be imported into the certificate store - "ca_certificate_file": "D:/Projects/nmos-testing/test_data/BCP00301/ca/certs/ca.cert.pem", - - // server_secure [registry, node]: whether server should listen for secure connection for communication (https and wss) - // e.g. typically false when using a reverse proxy, or the same as client_secure otherwise - // when true, server certificates etc. must also be configured - "server_secure": true, - - // server_certificates [registry, node]: an array of server certificate objects, each has the name of the key algorithm, the full paths of private key file and certificate chain file - // each value must be an object like { "key_algorithm": "ECDSA", "private_key_file": "server-ecdsa-key.pem", "certificate_chain_file": "server-ecdsa-chain.pem" } - // key_algorithm (attribute of server_certificates objects): name of the key algorithm for the certificate, see nmos::key_algorithm - // private_key_file (attribute of server_certificates objects): full path of private key file in PEM format - // certificate_chain_file (attribute of server_certificates object): full path of certificate chain file in PEM format, which must be sorted - // starting with the server's certificate, followed by any intermediate CA certificates, and ending with the highest level (root) CA - // on Windows, if C++ REST SDK is built with CPPREST_HTTP_LISTENER_IMPL=httpsys (reported as "listener=httpsys" by nmos::get_build_settings_info) - // one of the certificates must also be bound to each port e.g. using 'netsh add sslcert' - "server_certificates": [ - { - "key_algorithm": "ECDSA", - "private_key_file": "D:/Projects/nmos-testing/test_data/BCP00301/ca/intermediate/private/ecdsa.api.testsuite.nmos.tv.key.pem", - "certificate_chain_file": "D:/Projects/nmos-testing/test_data/BCP00301/ca/intermediate/certs/ecdsa.api.testsuite.nmos.tv.cert.chain.pem" - }, - { - "key_algorithm": "RSA", - "private_key_file": "D:/Projects/nmos-testing/test_data/BCP00301/ca/intermediate/private/rsa.api.testsuite.nmos.tv.key.pem", - "certificate_chain_file": "D:/Projects/nmos-testing/test_data/BCP00301/ca/intermediate/certs/rsa.api.testsuite.nmos.tv.cert.chain.pem" - }], - // validate_certificates [registry, node]: boolean value, false (ignore all server certificate validation errors), or true (do not ignore, the default behaviour) - "validate_certificates": true, - - // dh_param_file [registry, node]: Diffie-Hellman parameters file in PEM format for ephemeral key exchange support, or empty string for no support - "dh_param_file": "D:/Projects/nmos-testing/test_data/BCP00301/ca/intermediate/private/dhparam.pem", - - // system_interval_min/system_interval_max [node]: used to poll for System API changes; default is about one hour - //"system_interval_min": 3600, - //"system_interval_max": 3660, - - // hsts_max_age [registry, node]: the HTTP Strict-Transport-Security response header's max-age value; default is approximately 365 days - // (the header is omitted if server_secure is false, or hsts_max_age is negative) - // See https://tools.ietf.org/html/rfc6797#section-6.1.1 - //"hsts_max_age": 31536000, - - // hsts_include_sub_domains [registry, node]: the HTTP Strict-Transport-Security HTTP response header's includeSubDomains value - // See https://tools.ietf.org/html/rfc6797#section-6.1.2 - //"hsts_include_sub_domains": false, - - // ocsp_interval_min/ocsp_interval_max [registry, node]: used to poll for certificate status (OCSP) changes; default is about one hour - // Note that if half of the server certificate expiry time is shorter, then the ocsp_interval_min/max will be overridden by it - //"ocsp_interval_min": 3600, - //"ocsp_interval_max": 3660, - - // ocsp_request_max [registry, node]: timeout for interactions with the OCSP server - //"ocsp_request_max": 30, - - // authorization_address [registry, node]: IP address or host name used to construct request URLs for the Authorization API (if not discovered via DNS-SD) - "authorization_address": "nmos-mocks.local", - - // authorization_port [registry, node]: used to construct request URLs for the authorization server's Authorization API (if not discovered via DNS-SD) - "authorization_port": 5010, - - // authorization_version [registry, node]: used to construct request URLs for Authorization API (if not discovered via DNS-SD) - //"authorization_version": "v1.0", - - // authorization_selector [registry, node]: used to construct request URLs for the authorization API (if not discovered via DNS-SD) - //"authorization_selector", "", - - // authorization_request_max [registry, node]: timeout for interactions with the Authorization API /certs & /token endpoints - //"authorization_request_max": 30, - - // fetch_authorization_public_keys_interval_min/fetch_authorization_public_keys_interval_max [registry, node]: used to poll for Authorization API public keys changes; default is about one hour - // "Resource Servers (Nodes) SHOULD seek to fetch public keys from the Authorization Server at least once every hour. Resource Servers MUST vary their retrieval - // interval at random by up to at least one minute to avoid overloading the Authorization Server due to Resource Servers synchronising their retrieval time." - // See https://specs.amwa.tv/is-10/releases/v1.0.0/docs/4.1._Behaviour_-_Authorization_Servers.html#authorization-server-public-keys - //"fetch_authorization_public_keys_interval_min": 3600, - //"fetch_authorization_public_keys_interval_max": 3660, - - // access_token_refresh_interval [node]: time interval (in seconds) to refresh access token from Authorization Server - // It specified the access token refresh period otherwise Bearer token's expires_in is used instead. - // See https://specs.amwa.tv/is-10/releases/v1.0.0/docs/4.4._Behaviour_-_Access_Tokens.html#access-token-lifetime - //"access_token_refresh_interval": -1, - - // client_authorization [node]: whether clients should use authorization to access protected APIs - "client_authorization": true, - - // server_authorization [registry, node]: whether server should use authorization to protect its APIs - "server_authorization": true, - - // authorization_code_flow_max [node]: timeout for the authorization code workflow (in seconds) - // No timeout if value is set to -1, default to 30 seconds - //"authorization_code_flow_max": 30, - - // authorization_flow [node]: used to specify the authorization flow for the registered scopes - // supported flow are authorization_code and client_credentials - // client_credentials SHOULD only be used for NO user interface node, otherwise authorization_code MUST be used - "authorization_flow": "client_credentials", - - // authorization_redirect_port [node]: redirect URL port for listening authorization code, used for client registration - //"authorization_redirect_port": 3218, - - // initial_access_token [node]: initial access token giving access to the client registration endpoint for non-opened registration - //"initial_access_token", "", - - // authorization_scopes [node]: used to specify the supported scopes for client registration - // supported scopes are registration, query, node, connection, events and channelmapping - "authorization_scopes": [ "registration" ], - - // token_endpoint_auth_method [node]: String indicator of the requested authentication method for the token endpoint - // supported methods are none, client_secret_basic and private_key_jwt, default to client_secret_basic, where none is used for public client - //"token_endpoint_auth_method": "private_key_jwt", - - // jwks_uri_port [node]: JWKs URL port for providing JSON Web Key Set (public keys) to Authorization Server for verifing client_assertion, used for client registration - //"jwks_uri_port": 3218, - - // validate_openid_client [node]: boolean value, false (bypass openid connect client validation), or true (do not bypass, the default behaviour) - //"validate_openid_client": true, - - // no_trailing_dot_for_authorization_callback_uri [node]: used to specify whether no trailing dot FQDN should be used to construct the URL for the authorization server callbacks - // as it is because not all Authorization server can cope with URL with trailing dot, default to true - //"no_trailing_dot_for_authorization_callback_uri": true, - - // retry_after [registry, node]: used to specify the HTTP Retry-After header to indicate the number of seconds when the client may retry its request again, default to 5 seconds - // "Where a Resource Server has no matching public key for a given token, it SHOULD attempt to obtain the missing public key via the the token iss - // claim as specified in RFC 8414 section 3. In cases where the Resource Server needs to fetch a public key from a remote Authorization Server it - // MAY temporarily respond with an HTTP 503 code in order to avoid blocking the incoming authorized request. When a HTTP 503 code is used, the Resource - // Server SHOULD include an HTTP Retry-After header to indicate when the client may retry its request. - // If the Resource Server fails to verify a token using all public keys available it MUST reject the token." - //"service_unavailable_retry_after": 5, - - "don't worry": "about trailing commas" -} diff --git a/Development/nmos-cpp-node/config.jrt.json b/Development/nmos-cpp-node/config.jrt.json deleted file mode 100644 index 0e81e444a..000000000 --- a/Development/nmos-cpp-node/config.jrt.json +++ /dev/null @@ -1,311 +0,0 @@ -// Note: C++/JavaScript-style single and multi-line comments are permitted and ignored in nmos-cpp config files - -// Configuration settings and defaults -{ - // Custom settings for the example node implementation - - // node_tags, device_tags: used in resource tags fields - // "Each tag has a single key, but MAY have multiple values." - // See https://specs.amwa.tv/is-04/releases/v1.3.2/docs/APIs_-_Common_Keys.html#tags - // { - // "tag_1": [ "tag_1_value_1", "tag_1_value_2" ], - // "tag_2": [ "tag_2_value_1" ] - // } - //"node_tags": {}, - //"device_tags": {}, - - // how_many: provides for very basic testing of a node with many sub-resources of each type - //"how_many": 4, - - // activate_senders: controls whether to activate senders on start up (true, default) or not (false) - //"activate_senders": false, - - // senders, receivers: controls which kinds of sender and receiver are instantiated by the example node - // the values must be an array of unique strings identifying the kinds of 'port', like ["v", "a", "d"], see impl::ports - // when omitted, all ports are instantiated - //"senders": ["v", "a"], - //"receivers": [], - - // frame_rate: controls the grain_rate of video, audio and ancillary data sources and flows - // and the equivalent parameter constraint on video receivers - // the value must be an object like { "numerator": 25, "denominator": 1 } - //"frame_rate": { "numerator": 60000, "denominator": 1001 }, - - // frame_width, frame_height: control the frame_width and frame_height of video flows - //"frame_width": 3840, - //"frame_height": 2160, - - // interlace_mode: controls the interlace_mode of video flows, see nmos::interlace_mode - // when omitted, a default of "progressive" or "interlaced_tff" is used based on the frame_rate, etc. - //"interlace_mode": "progressive", - - // colorspace: controls the colorspace of video flows, see nmos::colorspace - //"colorspace": "BT709", - - // transfer_characteristic: controls the transfer characteristic system of video flows, see nmos::transfer_characteristic - //"transfer_characteristic": "SDR", - - // color_sampling: controls the color (sub-)sampling mode of video flows, see sdp::sampling - //"color_sampling": "YCbCr-4:2:2", - - // component_depth: controls the bits per component sample of video flows - //"component_depth": 10, - - // video_type: media type of video flows, e.g. "video/raw" or "video/jxsv", see nmos::media_types - //"video_type": "video/jxsv", - - // channel_count: controls the number of channels in audio sources - //"channel_count": 8, - - // smpte2022_7: controls whether senders and receivers have one leg (false) or two legs (true, default) - //"smpte2022_7": false, - - // Configuration settings and defaults for logging - - // error_log [registry, node]: filename for the error log or an empty string to write to stderr - //"error_log": "", - - // access_log [registry, node]: filename for the access log (in Common Log Format) or an empty string to discard - //"access_log": "", - - // logging_level [registry, node]: integer value, between 40 (least verbose, only fatal messages) and -40 (most verbose) - //"logging_level": 0, - - // logging_categories [registry, node]: array of logging categories to be included in the error log - //"logging_categories": ["node_implementation"], - - // Configuration settings and defaults for the NMOS APIs - - // host_name [registry, node]: the fully-qualified host name for which to advertise services, also used to construct response headers and fields in the data model - //"host_name": "", // when omitted or an empty string, the default is used - - // domain [registry, node]: the domain on which to browse for services or an empty string to use the default domain (specify "local." to explictly select mDNS) - //"domain": "", - - // host_address/host_addresses [registry, node]: IP addresses used to construct response headers (e.g. 'Link' or 'Location'), and host and URL fields in the data model - //"host_address": "127.0.0.1", - //"host_addresses": array-of-ip-address-strings, - - // is04_versions [registry, node]: used to specify the enabled API versions (advertised via 'api_ver') for a version-locked configuration - //"is04_versions": ["v1.2", "v1.3"], - - // is05_versions [node]: used to specify the enabled API versions for a version-locked configuration - //"is05_versions": ["v1.0", "v1.1"], - - // is07_versions [node]: used to specify the enabled API versions for a version-locked configuration - //"is07_versions": ["v1.0"], - - // is08_versions [node]: used to specify the enabled API versions for a version-locked configuration - //"is08_versions": ["v1.0"], - - // is09_versions [registry, node]: used to specify the enabled API versions for a version-locked configuration - //"is09_versions": ["v1.0"], - - // pri [registry, node]: used for the 'pri' TXT record; specifying nmos::service_priorities::no_priority (maximum value) disables advertisement completely - //"pri": 100, - - // highest_pri, lowest_pri [node]: used to specify the (inclusive) range of suitable 'pri' values of discovered APIs, to avoid development and live systems colliding - //"highest_pri": 0, - "highest_pri": 2147483647, - //"lowest_pri": 2147483647, - "lowest_pri": 2147483647, - - // discovery_backoff_min/discovery_backoff_max/discovery_backoff_factor [registry, node]: used to back-off after errors interacting with all discoverable service instances - // e.g. Registration APIs, System APIs, or OCSP servers - //"discovery_backoff_min": 1, - //"discovery_backoff_max": 30, - "discovery_backoff_factor": 1, - - // service_name_prefix [registry, node]: used as a prefix in the advertised service names ("__:", e.g. "nmos-cpp_node_127-0-0-1:3212") - //"service_name_prefix": "nmos-cpp" - - // registry_address [node]: IP address or host name used to construct request URLs for registry APIs (if not discovered via DNS-SD) - "registry_address": "43.195.121.126", - //"registry_address": "43.195.121.163", - //"registry_address": "registry.nmos-tb.bbctest01.uk", - - // registry_version [node]: used to construct request URLs for registry APIs (if not discovered via DNS-SD) - //"registry_version": "v1.2", - - // port numbers [registry, node]: ports to which clients should connect for each API - - // http_port [registry, node]: if specified, this becomes the default port for each HTTP API and the next higher port becomes the default for each WebSocket API - "http_port": 7000, - - // registration_port [node]: used to construct request URLs for the registry's Registration API (if not discovered via DNS-SD) - "registration_port": 80, - //"registration_port": 8010, - //"node_port": 3212, - //"connection_port": 3215, - //"events_port": 3216, - //"events_ws_port": 3217, - //"channelmapping_port": 3215, - // system_port [node]: used to construct request URLs for the System API (if not discovered via DNS-SD) - //"system_port": 10641, - //"control_protocol_ws_port": 3218, - - // listen_backlog [registry, node]: the maximum length of the queue of pending connections, or zero for the implementation default (the implementation may not honour this value) - //"listen_backlog": 0, - - // registration_heartbeat_interval [registry, node]: - // "Nodes are expected to peform a heartbeat every 5 seconds by default." - // See https://specs.amwa.tv/is-04/releases/v1.2.0/docs/4.1._Behaviour_-_Registration.html#heartbeating - //"registration_heartbeat_interval": 5, - - // registration_request_max [node]: timeout for interactions with the Registration API /resource endpoint - //"registration_request_max": 30, - - // registration_heartbeat_max [node]: timeout for interactions with the Registration API /health/nodes endpoint - // Note that the default timeout is the same as the default heartbeat interval, in order that there is then a reasonable opportunity to try the next available Registration API - // though in some circumstances registration expiry could potentially still be avoided with a timeout that is (almost) twice the garbage collection interval... - //"registration_heartbeat_max": 5, - - // immediate_activation_max [node]: timeout for immediate activations within the Connection API /staged endpoint - //"immediate_activation_max": 30, - - // events_heartbeat_interval [node, client]: - // "Upon connection, the client is required to report its health every 5 seconds in order to maintain its session and subscription." - // See https://specs.amwa.tv/is-07/releases/v1.0.1/docs/5.2._Transport_-_Websocket.html#41-heartbeats - //"events_heartbeat_interval": 5, - - // events_expiry_interval [node]: - // "The server is expected to check health commands and after a 12 seconds timeout (2 consecutive missed health commands plus 2 seconds to allow for latencies) - // it should clear the subscriptions for that particular client and close the websocket connection." - // See https://specs.amwa.tv/is-07/releases/v1.0.1/docs/5.2._Transport_-_Websocket.html#41-heartbeats - //"events_expiry_interval": 12, - - // system_address [node]: IP address or host name used to construct request URLs for the System API (if not discovered via DNS-SD) - //"system_address": ip-address-string, - - // system_version [node]: used to construct request URLs for the System API (if not discovered via DNS-SD) - //"system_version": "v1.0", - - // system_request_max [node]: timeout for interactions with the System API - //"system_request_max": 30, - - // Configuration settings and defaults for experimental extensions - - // seed id [registry, node]: optional, used to generate repeatable id values when running with the same configuration - "seed_id": "35aa7ef6-fb24-11ed-8ea8-88c9e8acd926", - - // label [registry, node]: used in resource label field - "label": "NMOS CPP Node with Receiver Monitors", - - // description [registry, node]: used in resource description field - "description": "NMOS CPP Node with Receiver Monitors", - - // port numbers [registry, node]: ports to which clients should connect for each API - // see http_port - - //"settings_port": 3209, - //"logging_port": 5106, - - // addresses [registry, node]: IP addresses on which to listen for each API, or empty string for the wildcard address - - // server_address [registry, node]: if specified, this becomes the default address on which to listen for each API instead of the wildcard address - //"server_address": "", - - // addresses [registry, node]: IP addresses on which to listen for specific APIs - - //"settings_address": "127.0.0.1", - //"logging_address": "", - - // client_address [registry, node]: IP address of the network interface to bind client connections - // for now, only supporting HTTP/HTTPS client connections on Linux - //"client_address": "", - - // logging_limit [registry, node]: maximum number of log events cached for the Logging API - //"logging_limit": 1234, - - // logging_paging_default/logging_paging_limit [registry, node]: default/maximum number of results per "page" when using the Logging API (a client may request a lower limit) - //"logging_paging_default": 100, - //"logging_paging_limit": 100, - - // http_trace [registry, node]: whether server should enable (default) or disable support for HTTP TRACE - //"http_trace": true, - - // proxy_map [registry, node]: mapping between the port numbers to which the client connects, and the port numbers on which the server should listen, if different - // for use with a reverse proxy; each element of the array is an object like { "client_port": 80, "server_port": 8080 } - //"proxy_map": array-of-mappings, - - // proxy_address [registry, node]: address of the forward proxy to use when making HTTP requests or WebSocket connections, or an empty string for no proxy - //"proxy_address": "127.0.0.1", - - // proxy_port [registry, node]: forward proxy port - //"proxy_port": 8080, - - // discovery_mode [node]: whether the discovered host name (1) or resolved addresses (2) are used to construct request URLs for Registration APIs or System APIs - //"discovery_mode": 1, - - // href_mode [registry, node]: whether the host name (1), addresses (2) or both (3) are used to construct response headers, and host and URL fields in the data model - //"href_mode": 1, - - // client_secure [registry, node]: whether clients should use a secure connection for communication (https and wss) - // when true, CA root certificates must also be configured - //"client_secure": false, - - // ca_certificate_file [registry, node]: full path of certification authorities file in PEM format - // on Windows, if C++ REST SDK is built with CPPREST_HTTP_CLIENT_IMPL=winhttp (reported as "client=winhttp" by nmos::get_build_settings_info) - // the trusted root CA certificates must also be imported into the certificate store - //"ca_certificate_file": "ca.pem", - - // server_secure [registry, node]: whether server should listen for secure connection for communication (https and wss) - // e.g. typically false when using a reverse proxy, or the same as client_secure otherwise - // when true, server certificates etc. must also be configured - //"server_secure": false, - - // server_certificates [registry, node]: an array of server certificate objects, each has the name of the key algorithm, the full paths of private key file and certificate chain file - // each value must be an object like { "key_algorithm": "ECDSA", "private_key_file": "server-ecdsa-key.pem", "certificate_chain_file": "server-ecdsa-chain.pem" } - // key_algorithm (attribute of server_certificates objects): name of the key algorithm for the certificate, see nmos::key_algorithm - // private_key_file (attribute of server_certificates objects): full path of private key file in PEM format - // certificate_chain_file (attribute of server_certificates object): full path of certificate chain file in PEM format, which must be sorted - // starting with the server's certificate, followed by any intermediate CA certificates, and ending with the highest level (root) CA - // on Windows, if C++ REST SDK is built with CPPREST_HTTP_LISTENER_IMPL=httpsys (reported as "listener=httpsys" by nmos::get_build_settings_info) - // one of the certificates must also be bound to each port e.g. using 'netsh add sslcert' - //"server_certificates": [{"key_algorithm": "ECDSA", "private_key_file": "server-ecdsa-key.pem", "certificate_chain_file": "server-ecdsa-chain.pem"}, {"key_algorithm": "RSA", "private_key_file": "server-rsa-key.pem", "certificate_chain_file": "server-rsa-chain.pem"}], - - // validate_certificates [registry, node]: boolean value, false (ignore all server certificate validation errors), or true (do not ignore, the default behaviour) - //"validate_certificates": true, - - // dh_param_file [registry, node]: Diffie-Hellman parameters file in PEM format for ephemeral key exchange support, or empty string for no support - //"dh_param_file": "dhparam.pem", - - // system_interval_min/system_interval_max [node]: used to poll for System API changes; default is about one hour - //"system_interval_min": 3600, - //"system_interval_max": 3660, - - // hsts_max_age [registry, node]: the HTTP Strict-Transport-Security response header's max-age value; default is approximately 365 days - // (the header is omitted if server_secure is false, or hsts_max_age is negative) - // See https://tools.ietf.org/html/rfc6797#section-6.1.1 - //"hsts_max_age": 31536000, - - // hsts_include_sub_domains [registry, node]: the HTTP Strict-Transport-Security HTTP response header's includeSubDomains value - // See https://tools.ietf.org/html/rfc6797#section-6.1.2 - //"hsts_include_sub_domains": false, - - // ocsp_interval_min/ocsp_interval_max [registry, node]: used to poll for certificate status (OCSP) changes; default is about one hour - // Note that if half of the server certificate expiry time is shorter, then the ocsp_interval_min/max will be overridden by it - //"ocsp_interval_min": 3600, - //"ocsp_interval_max": 3660, - - // ocsp_request_max [registry, node]: timeout for interactions with the OCSP server - //"ocsp_request_max": 30, - - // manufacturer_name [node]: the manufacturer name of the NcDeviceManager used for NMOS Control Protocol - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager - "manufacturer_name": "Sony", - - // product_name/product_key/product_revision_level [node]: the product description of the NcDeviceManager used for NMOS Control Protocol - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncproduct - "product_name": "NMOS-CPP", - //"product_key": "", - //"product_revision_level": "", - - "simulate_status_monitor_activity": false, - - // serial_number [node]: the serial number of the NcDeviceManager used for NMOS Control Protocol - // See https://specs.amwa.tv/ms-05-02/branches/v1.0-dev/docs/Framework.html#ncdevicemanager - "serial_number": "123456789", - - "don't worry": "about trailing commas" -} From 321c4fd09dbaeb589755bf9b88f424f0d3d055cb Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Wed, 24 Sep 2025 11:58:18 +0100 Subject: [PATCH 248/250] Deprecate old style function names --- .../nmos/control_protocol_resource.cpp | 16 +- Development/nmos/control_protocol_resource.h | 196 +++++++++++++++++- Development/nmos/control_protocol_utils.cpp | 8 +- Development/nmos/control_protocol_utils.h | 78 +++++++ Development/nmos/control_protocol_ws_api.cpp | 12 +- 5 files changed, 286 insertions(+), 24 deletions(-) diff --git a/Development/nmos/control_protocol_resource.cpp b/Development/nmos/control_protocol_resource.cpp index 015892157..a58a8826b 100644 --- a/Development/nmos/control_protocol_resource.cpp +++ b/Development/nmos/control_protocol_resource.cpp @@ -1004,7 +1004,7 @@ namespace nmos // command message response // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#command-response-message-type - web::json::value make_control_protocol_response(int32_t handle, const web::json::value& method_result) + web::json::value make_response(int32_t handle, const web::json::value& method_result) { using web::json::value_of; @@ -1013,7 +1013,7 @@ namespace nmos { nmos::fields::nc::result, method_result } }); } - web::json::value make_control_protocol_command_response(const web::json::value& responses) + web::json::value make_command_response(const web::json::value& responses) { using web::json::value_of; @@ -1025,7 +1025,7 @@ namespace nmos // subscription response // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#subscription-response-message-type - web::json::value make_control_protocol_subscription_response(const web::json::value& subscriptions) + web::json::value make_subscription_response(const web::json::value& subscriptions) { using web::json::value_of; @@ -1038,7 +1038,7 @@ namespace nmos // notification // See https://specs.amwa.tv/ms-05-01/branches/v1.0.x/docs/Core_Mechanisms.html#notification-messages // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#notification-message-type - web::json::value make_control_protocol_notification(nc_oid oid, const nc_event_id& event_id, const nc_property_changed_event_data& property_changed_event_data) + web::json::value make_notification(nc_oid oid, const nc_event_id& event_id, const nc_property_changed_event_data& property_changed_event_data) { using web::json::value_of; @@ -1048,7 +1048,7 @@ namespace nmos { nmos::fields::nc::event_data, details::make_property_changed_event_data(property_changed_event_data) } }); } - web::json::value make_control_protocol_notification_message(const web::json::value& notifications) + web::json::value make_notification_message(const web::json::value& notifications) { using web::json::value_of; @@ -1068,14 +1068,14 @@ namespace nmos auto notifications = value::array(); for (auto& property_changed_event_data : property_changed_event_data_list) { - web::json::push_back(notifications, make_control_protocol_notification(oid, nc_object_property_changed_event_id, property_changed_event_data)); + web::json::push_back(notifications, make_notification(oid, nc_object_property_changed_event_id, property_changed_event_data)); } - return make_control_protocol_notification_message(notifications); + return make_notification_message(notifications); } // error message // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#error-messages - web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message) + web::json::value make_error_message(const nc_method_result& method_result, const utility::string_t& error_message) { using web::json::value_of; diff --git a/Development/nmos/control_protocol_resource.h b/Development/nmos/control_protocol_resource.h index eebf4fd75..902b995b0 100644 --- a/Development/nmos/control_protocol_resource.h +++ b/Development/nmos/control_protocol_resource.h @@ -224,18 +224,18 @@ namespace nmos // command message response // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#command-response-message-type - web::json::value make_control_protocol_response(int32_t handle, const web::json::value& method_result); - web::json::value make_control_protocol_command_response(const web::json::value& responses); + web::json::value make_response(int32_t handle, const web::json::value& method_result); + web::json::value make_command_response(const web::json::value& responses); // subscription response // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#subscription-response-message-type - web::json::value make_control_protocol_subscription_response(const web::json::value& subscriptions); + web::json::value make_subscription_response(const web::json::value& subscriptions); // notification // See https://specs.amwa.tv/ms-05-01/branches/v1.0.x/docs/Core_Mechanisms.html#notification-messages // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#notification-message-type - web::json::value make_control_protocol_notification(nc_oid oid, const nc_event_id& event_id, const nc_property_changed_event_data& property_changed_event_data); - web::json::value make_control_protocol_notification_message(const web::json::value& notifications); + web::json::value make_notification(nc_oid oid, const nc_event_id& event_id, const nc_property_changed_event_data& property_changed_event_data); + web::json::value make_notification_message(const web::json::value& notifications); // property changed notification event // See https://specs.amwa.tv/ms-05-01/branches/v1.0.x/docs/Core_Mechanisms.html#the-propertychanged-event @@ -244,7 +244,7 @@ namespace nmos // error message // See https://specs.amwa.tv/is-12/branches/v1.0.x/docs/Protocol_messaging.html#error-messages - web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message); + web::json::value make_error_message(const nc_method_result& method_result, const utility::string_t& error_message); // Control class models // See https://specs.amwa.tv/ms-05-02/branches/v1.0.x/models/classes/#control-class-models-for-branch-v10-dev @@ -503,5 +503,189 @@ namespace nmos // See https://specs.amwa.tv/nmos-control-feature-sets/branches/main/device-configuration/#ncmethodresultobjectpropertiessetvalidation web::json::value make_method_result_object_properties_set_validation_datatype(); } + + namespace details + { + // Deprecated : use nc::details::make_method_result + inline web::json::value make_nc_method_result(const nc_method_result& method_result) { return nc::details::make_method_result(method_result); }; + inline web::json::value make_nc_method_result_error(const nc_method_result& method_result, const utility::string_t& error_message) { return nc::details::make_method_result_error(method_result, error_message); }; + inline web::json::value make_nc_method_result(const nc_method_result& method_result, const web::json::value& value) { return nc::details::make_method_result(method_result, value); }; + + // Deprecated: use nc::details::make use nc::details::make_element_id + inline web::json::value make_nc_element_id(const nc_element_id& element_id) { return nc::details::make_element_id(element_id); }; + // Deprecated: use nc::details::make use nc::details::parse_element_id + inline nc_element_id parse_nc_element_id(const web::json::value& element_id) { return nc::details::parse_element_id(element_id); }; + + // Deprecated: use nc::details::make use nc::details::make_event_id + inline web::json::value make_nc_event_id(const nc_event_id& event_id) { return nc::details::make_event_id(event_id); }; + // Deprecated use nc::details::parse_event_id + inline nc_event_id parse_nc_event_id(const web::json::value& event_id) { return nc::details::parse_event_id(event_id); }; + + // Deprecated: use nc::details::make use nc::details::make_method_id + inline web::json::value make_nc_method_id(const nc_method_id& method_id) { return nc::details::make_method_id(method_id); }; + // Deprecated: use nc::details::parse_method_id + inline nc_method_id parse_nc_method_id(const web::json::value& method_id) { return nc::details::parse_method_id(method_id); }; + + // Deprecated: use nc::details::make_property_id + inline web::json::value make_nc_property_id(const nc_property_id& property_id) { return nc::details::make_property_id(property_id); }; + // Deprecated: use nc::details::parse_property_id + inline nc_property_id parse_nc_property_id(const web::json::value& property_id) { return nc::details::parse_property_id(property_id); }; + + // Deprecated: use nc::details::make_class_id + inline web::json::value make_nc_class_id(const nc_class_id& class_id) { return nc::details::make_class_id(class_id); }; + // Deprecated: use nc::details::make + inline nc_class_id parse_nc_class_id(const web::json::array& class_id) { return nc::details::parse_class_id(class_id); }; + + // Deprecated: use nc::details::make_manufacturer + inline web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id, const web::uri& website) { return nc::details::make_manufacturer(name, organization_id, website); }; + inline web::json::value make_nc_manufacturer(const utility::string_t& name, nc_organization_id organization_id) { return nc::details::make_manufacturer(name, organization_id); }; + inline web::json::value make_nc_manufacturer(const utility::string_t& name) { return nc::details::make_manufacturer(name); }; + + // Deprecated: use nc::details::make_product + inline web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const utility::string_t& brand_name, const nc_uuid& uuid, const utility::string_t& description) { return nc::details::make_product(name, key, revision_level, brand_name, uuid, description); }; + inline web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const utility::string_t& brand_name, const nc_uuid& uuid) { return nc::details::make_product(name, key, revision_level, brand_name, uuid); }; + inline web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level, + const utility::string_t& brand_name) { return nc::details::make_product(name, key, revision_level, brand_name); }; + inline web::json::value make_nc_product(const utility::string_t& name, const utility::string_t& key, const utility::string_t& revision_level) { return nc::details::make_product(name, key, revision_level); }; + + // Deprecated: use nc::details::make_device_operational_state + inline web::json::value make_nc_device_operational_state(nc_device_generic_state::state generic_state, const web::json::value& device_specific_details) { return nc::details::make_device_operational_state(generic_state, device_specific_details); }; + + // Deprecated: use nc::details::make_block_member_descriptor + inline web::json::value make_nc_block_member_descriptor(const utility::string_t& description, const utility::string_t& role, nc_oid oid, bool constant_oid, const nc_class_id& class_id, const utility::string_t& user_label, nc_oid owner) { return nc::details::make_block_member_descriptor(description, role, oid, constant_oid, class_id, user_label, owner); }; + + // Deprecated: use nc::details::make_class_descriptor + inline web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const utility::string_t& fixed_role, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) { return nc::details::make_class_descriptor(description, class_id, name, fixed_role, properties, methods, events); }; + inline web::json::value make_nc_class_descriptor(const utility::string_t& description, const nc_class_id& class_id, const nc_name& name, const web::json::value& properties, const web::json::value& methods, const web::json::value& events) { return nc::details::make_class_descriptor(description, class_id, name, properties, methods, events); }; + + // Deprecated: use nc::details::make_enum_item_descriptor + inline web::json::value make_nc_enum_item_descriptor(const utility::string_t& description, const nc_name& name, uint16_t val) { return nc::details::make_enum_item_descriptor(description, name, val); }; + + // Deprecated: use nc::details::make_event_descriptor + inline web::json::value make_nc_event_descriptor(const utility::string_t& description, const nc_event_id& id, const nc_name& name, const utility::string_t& event_datatype, bool is_deprecated) { return nc::details::make_event_descriptor(description, id, name, event_datatype, is_deprecated); }; + + // Deprecated: use nc::details::make_field_descriptor + inline web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { return nc::details::make_field_descriptor(description, name, type_name, is_nullable, is_sequence, constraints); }; + inline web::json::value make_nc_field_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { return nc::details::make_field_descriptor(description, name, is_nullable, is_sequence, constraints); }; + + // Deprecated: use nc::details::make_method_descriptor + inline web::json::value make_nc_method_descriptor(const utility::string_t& description, const nc_method_id& id, const nc_name& name, const utility::string_t& result_datatype, const web::json::value& parameters, bool is_deprecated) { return nc::details::make_method_descriptor(description, id, name, result_datatype, parameters, is_deprecated); }; + + // Deprecated: use nc::details::make_parameter_descriptor + inline web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { return nc::details::make_parameter_descriptor(description, name, is_nullable, is_sequence, constraints); }; + inline web::json::value make_nc_parameter_descriptor(const utility::string_t& description, const nc_name& name, const utility::string_t& type_name, bool is_nullable, bool is_sequence, const web::json::value& constraints) { return nc::details::make_parameter_descriptor(description, name, type_name, is_nullable, is_sequence, constraints); }; + + // Deprecated: use nc::details::make_property_descriptor + inline web::json::value make_nc_property_descriptor(const utility::string_t& description, const nc_property_id& id, const nc_name& name, const utility::string_t& type_name, + bool is_read_only, bool is_nullable, bool is_sequence, bool is_deprecated, const web::json::value& constraints) { return nc::details::make_property_descriptor(description, id, name, type_name, is_read_only, is_nullable, is_sequence, is_deprecated, constraints); }; + + // Deprecated: use nc::details::make_datatype_descriptor_enum + inline web::json::value make_nc_datatype_descriptor_enum(const utility::string_t& description, const nc_name& name, const web::json::value& items, const web::json::value& constraints) { return nc::details::make_datatype_descriptor_enum(description, name, items, constraints); }; + + // Deprecated: use nc::details::make_datatype_descriptor_primitive + inline web::json::value make_nc_datatype_descriptor_primitive(const utility::string_t& description, const nc_name& name, const web::json::value& constraints) { return nc::details::make_datatype_descriptor_primitive(description, name, constraints); }; + + // Deprecated: use nc::details::make_datatype_descriptor_struct + inline web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const utility::string_t& parent_type, const web::json::value& constraints) { return nc::details::make_datatype_descriptor_struct(description, name, fields, parent_type, constraints); }; + inline web::json::value make_nc_datatype_descriptor_struct(const utility::string_t& description, const nc_name& name, const web::json::value& fields, const web::json::value& constraints) { return nc::details::make_datatype_descriptor_struct(description, name, fields, constraints); }; + + // Deprecated: use nc::details::make_datatype_typedef + inline web::json::value make_nc_datatype_typedef(const utility::string_t& description, const nc_name& name, bool is_sequence, const utility::string_t& parent_type, const web::json::value& constraints) { return nc::details::make_datatype_typedef(description, name, is_sequence, parent_type, constraints); }; + + // Deprecated: use nc::details::make_property_constraints + inline web::json::value make_nc_property_constraints(const nc_property_id& property_id, const web::json::value& default_value) { return nc::details::make_property_constraints(property_id, default_value); }; + + // Deprecated: use nc::details::make_property_constraints_number + inline web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step) { return nc::details::make_property_constraints_number(property_id, default_value, minimum, maximum, step); }; + inline web::json::value make_nc_property_constraints_number(const nc_property_id& property_id, uint64_t minimum, uint64_t maximum, uint64_t step) { return nc::details::make_property_constraints_number(property_id, minimum, maximum, step); }; + + // Deprecated: use nc::details::make_property_constraints_string + inline web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern) { return nc::details::make_property_constraints_string(property_id, default_value, max_characters, pattern); }; + inline web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters, const nc_regex& pattern) { return nc::details::make_property_constraints_string(property_id, max_characters, pattern); }; + inline web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, uint32_t max_characters) { return nc::details::make_property_constraints_string(property_id, max_characters); }; + inline web::json::value make_nc_property_constraints_string(const nc_property_id& property_id, const nc_regex& pattern) { return nc::details::make_property_constraints_string(property_id, pattern); }; + + // Deprecated: use nc::details::make_parameter_constraints + inline web::json::value make_nc_parameter_constraints(const web::json::value& default_value) { return nc::details::make_parameter_constraints(default_value); }; + + // Deprecated: use nc::details::make_parameter_constraints_number + inline web::json::value make_nc_parameter_constraints_number(uint64_t default_value, uint64_t minimum, uint64_t maximum, uint64_t step) { return nc::details::make_parameter_constraints_number(default_value, minimum, maximum, step); }; + inline web::json::value make_nc_parameter_constraints_number(uint64_t minimum, uint64_t maximum, uint64_t step) { return nc::details::make_parameter_constraints_number(minimum, maximum, step); }; + + // Deprecated: use nc::details::make_parameter_constraints_string + inline web::json::value make_nc_parameter_constraints_string(const utility::string_t& default_value, uint32_t max_characters, const nc_regex& pattern) { return nc::details::make_parameter_constraints_string(default_value, max_characters, pattern); }; + inline web::json::value make_nc_parameter_constraints_string(uint32_t max_characters, const nc_regex& pattern) { return nc::details::make_parameter_constraints_string(max_characters, pattern); }; + inline web::json::value make_nc_parameter_constraints_string(uint32_t max_characters) { return nc::details::make_parameter_constraints_string(max_characters); }; + inline web::json::value make_nc_parameter_constraints_string(const nc_regex& pattern) { return nc::details::make_parameter_constraints_string(pattern); }; + + // Deprecated: use nc::details::make_touchpoint + inline web::json::value make_nc_touchpoint(const utility::string_t& context_namespace) { return nc::details::make_touchpoint(context_namespace); }; + + // Deprecated: use nc::details::make_touchpoint_nmos + inline web::json::value make_nc_touchpoint_nmos(const nc_touchpoint_resource_nmos& resource) { return nc::details::make_touchpoint_nmos(resource); }; + + // Deprecated: use nc::details::make_touchpoint_nmos_channel_mapping + inline web::json::value make_nc_touchpoint_nmos_channel_mapping(const nc_touchpoint_resource_nmos_channel_mapping& resource) { return nc::details::make_touchpoint_nmos_channel_mapping(resource); }; + + // Deprecated: use nc::details::make_object + inline web::json::value make_nc_object(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { return nc::details::make_object(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); }; + + // Deprecated: use nc::details::make_block + inline web::json::value make_nc_block(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled, const web::json::value& members) { return nc::details::make_block(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints, enabled, members); }; + + // Deprecated: use nc::details::make_worker + inline web::json::value make_nc_worker(const nc_class_id& class_id, nc_oid oid, bool constant_oid, nc_oid owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, bool enabled) { return nc::details::make_worker(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints, enabled); }; + + // Deprecated: use nc::details::make_manager + inline web::json::value make_nc_manager(const nc_class_id& class_id, nc_oid oid, bool constant_oid, const web::json::value& owner, const utility::string_t& role, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints) { return nc::details::make_manager(class_id, oid, constant_oid, owner, role, user_label, description, touchpoints, runtime_property_constraints); }; + + // Deprecated: use nc::details::make_device_manager + inline web::json::value make_nc_device_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, + const web::json::value& manufacturer, const web::json::value& product, const utility::string_t& serial_number, + const web::json::value& user_inventory_code, const web::json::value& device_name, const web::json::value& device_role, const web::json::value& operational_state, nc_reset_cause::cause reset_cause) { return nc::details::make_device_manager(oid, owner, user_label, description, touchpoints, runtime_property_constraints, manufacturer, product, serial_number, user_inventory_code, device_name, device_role, operational_state, reset_cause); }; + + // Deprecated: use nc::details::make_class_manager + inline web::json::value make_nc_class_manager(nc_oid oid, nc_oid owner, const web::json::value& user_label, const utility::string_t& description, const web::json::value& touchpoints, const web::json::value& runtime_property_constraints, const nmos::experimental::control_protocol_state& control_protocol_state) { return nc::details::make_class_manager(oid, owner, user_label, description, touchpoints, runtime_property_constraints, control_protocol_state); }; + } + + // Deprecated: use nc::make_response + inline web::json::value make_control_protocol_response(int32_t handle, const web::json::value& method_result) { return nc::make_response(handle, method_result); }; + // Deprecated: use nc::make_command_response + inline web::json::value make_control_protocol_command_response(const web::json::value& responses) { return nc::make_command_response(responses); }; + + // Deprecated: use nc::make_subscription_response + inline web::json::value make_control_protocol_subscription_response(const web::json::value& subscriptions) { return nc::make_subscription_response(subscriptions); }; + + // Deprecated: use nc::make_notification + inline web::json::value make_control_protocol_notification(nc_oid oid, const nc_event_id& event_id, const nc_property_changed_event_data& property_changed_event_data) { return nc::make_notification(oid, event_id, property_changed_event_data); }; + // Deprecated: use nc::make_notification_message + inline web::json::value make_control_protocol_notification_message(const web::json::value& notifications) { return nc::make_notification_message(notifications); }; + + // Deprecated: use nc::make_property_changed_event + inline web::json::value make_property_changed_event(nc_oid oid, const std::vector& property_changed_event_data_list) { return nc::make_property_changed_event(oid, property_changed_event_data_list); }; + + // Deprecated: use nc::make_error_message + inline web::json::value make_control_protocol_error_message(const nc_method_result& method_result, const utility::string_t& error_message) { return nc::make_error_message(method_result, error_message); }; + + // Deprecated: use nc::make_object_class + inline web::json::value make_nc_object_class() { return nc::make_object_class(); }; + // Deprecated: use nc::make_block_class + inline web::json::value make_nc_block_class() { return nc::make_block_class(); }; + // Deprecated: use nc::make_worker_class + inline web::json::value make_nc_worker_class() { return nc::make_worker_class(); }; + // Deprecated: use nc::make_manager_class + inline web::json::value make_nc_manager_class() { return nc::make_manager_class(); }; + // Deprecated: use nc::make_device_manager_class + inline web::json::value make_nc_device_manager_class() { return nc::make_device_manager_class(); }; + // Deprecated: use nc::make_class_manager_class + inline web::json::value make_nc_class_manager_class() { return nc::make_class_manager_class(); }; + // Deprecated: use nc::make_ident_beacon_class + inline web::json::value make_nc_ident_beacon_class() { return nc::make_ident_beacon_class(); }; + // Deprecated: use nc::make_receiver_monitor_class + inline web::json::value make_nc_receiver_monitor_class() { return nc::make_receiver_monitor_class(); }; + // Deprecated: use nc::make_sender_monitor_class + inline web::json::value make_nc_sender_monitor_class() { return nc::make_sender_monitor_class(); }; } #endif diff --git a/Development/nmos/control_protocol_utils.cpp b/Development/nmos/control_protocol_utils.cpp index fc1416c76..a9fd83d24 100644 --- a/Development/nmos/control_protocol_utils.cpp +++ b/Development/nmos/control_protocol_utils.cpp @@ -724,7 +724,7 @@ namespace nmos const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != found) { - get_member_descriptors(resources, *found, recurse, descriptors); + nc::get_member_descriptors(resources, *found, recurse, descriptors); } } } @@ -778,7 +778,7 @@ namespace nmos const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != found) { - find_members_by_role(resources, *found, role, match_whole_string, case_sensitive, recurse, descriptors); + nc::find_members_by_role(resources, *found, role, match_whole_string, case_sensitive, recurse, descriptors); } } } @@ -826,7 +826,7 @@ namespace nmos const auto& found = find_resource(resources, utility::s2us(std::to_string(oid))); if (resources.end() != found) { - find_members_by_class_id(resources, *found, class_id_, include_derived, recurse, descriptors); + nc::find_members_by_class_id(resources, *found, class_id_, include_derived, recurse, descriptors); } } } @@ -923,7 +923,7 @@ namespace nmos if (web::json::value::null() != notification_event && result) { auto& modified = *found; - insert_notification_events(resources, modified.version, modified.downgrade_version, modified.type, pre, modified.data, notification_event); + nc::insert_notification_events(resources, modified.version, modified.downgrade_version, modified.type, pre, modified.data, notification_event); } if (modifier_exception) diff --git a/Development/nmos/control_protocol_utils.h b/Development/nmos/control_protocol_utils.h index 8e3a683d7..7e11c4f2d 100644 --- a/Development/nmos/control_protocol_utils.h +++ b/Development/nmos/control_protocol_utils.h @@ -191,6 +191,84 @@ namespace nmos // Get property by name, rather than by property id. Used to get "hidden" resource properties web::json::value get_property(const resources& resources, nc_oid oid, const utility::string_t& property_name, slog::base_gate& gate); } + + namespace details + { + // Deprecated: use nc::details::get_runtime_property_constraints + inline web::json::value get_runtime_property_constraints(const nc_property_id& property_id, const web::json::value& runtime_property_constraints_list) { return nc::details::get_runtime_property_constraints(property_id, runtime_property_constraints_list); }; + + // Deprecated: use nc::details::get_datatype_descriptor + inline web::json::value get_datatype_descriptor(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype) { return nc::details::get_datatype_descriptor(type_name, get_control_protocol_datatype); }; + + // Deprecated: use nc::details::get_datatype_constraints + inline web::json::value get_datatype_constraints(const web::json::value& type_name, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype) { return nc::details::get_datatype_constraints(type_name, get_control_protocol_datatype); }; + + struct datatype_constraints_validation_parameters + { + web::json::value datatype_descriptor; + get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor; + }; + // Deprecated: use nc::details::constraints_validation + inline void constraints_validation(const web::json::value& value, const web::json::value& runtime_property_constraints, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) { return nc::details::constraints_validation(value, runtime_property_constraints, property_constraints, nc::details::datatype_constraints_validation_parameters{params.datatype_descriptor, params.get_control_protocol_datatype_descriptor}); }; + + // Deprecated: use nc::details::method_parameter_constraints_validation + inline void method_parameter_constraints_validation(const web::json::value& data, const web::json::value& property_constraints, const datatype_constraints_validation_parameters& params) { return nc::details::method_parameter_constraints_validation(data, property_constraints, nc::details::datatype_constraints_validation_parameters{params.datatype_descriptor, params.get_control_protocol_datatype_descriptor}); }; + } + + // Deprecated: use nc::is_block + inline bool is_nc_block(const nc_class_id& class_id) { return nc::is_block(class_id); }; + + // Deprecated: use nc::is_worker + inline bool is_nc_worker(const nc_class_id& class_id) { return nc::is_worker(class_id); }; + + // Deprecated: use nc::is_manager + inline bool is_nc_manager(const nc_class_id& class_id) { return nc::is_manager(class_id); }; + + // Deprecated: use nc::is_device_manager + inline bool is_nc_device_manager(const nc_class_id& class_id) { return nc::is_device_manager(class_id); }; + + // Deprecated: use nc::is_class_manager + inline bool is_nc_class_manager(const nc_class_id& class_id) { return nc::is_class_manager(class_id); }; + + // Deprecated: use nc::make_class_id + inline nc_class_id make_nc_class_id(const nc_class_id& prefix, int32_t authority_key, const std::vector& suffix) { return nc::make_class_id(prefix, authority_key, suffix); }; + inline nc_class_id make_nc_class_id(const nc_class_id& prefix, const std::vector& suffix) { return nc::make_class_id(prefix, suffix); }; + + // Deprecated: use nc::find_property_descriptor + inline web::json::value find_property_descriptor(const nc_property_id& property_id, const nc_class_id& class_id, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor) { return nc::find_property_descriptor(property_id, class_id, get_control_protocol_class_descriptor); }; + + // Deprecated: use nc::get_member_descriptors + inline void get_member_descriptors(const resources& resources, const resource& resource, bool recurse, web::json::array& descriptors) { return nc::get_member_descriptors(resources, resource, recurse, descriptors); }; + + // Deprecated: use nc::find_members_by_role + inline void find_members_by_role(const resources& resources, const resource& resource, const utility::string_t& role, bool match_whole_string, bool case_sensitive, bool recurse, web::json::array& nc_block_member_descriptors) { return nc::find_members_by_role(resources, resource, role, match_whole_string, case_sensitive, recurse, nc_block_member_descriptors); }; + + // Deprecated: use nc::find_members_by_class_id + inline void find_members_by_class_id(const resources& resources, const resource& resource, const nc_class_id& class_id, bool include_derived, bool recurse, web::json::array& descriptors) { return nc::find_members_by_class_id(resources, resource, class_id, include_derived, recurse, descriptors); }; + + // Deprecated: use nc::push_back + inline void push_back(control_protocol_resource& nc_block_resource, const control_protocol_resource& resource) { return nc::push_back(nc_block_resource, resource); }; + + // Deprecated: use nc::insert_resource + inline std::pair insert_control_protocol_resource(resources& resources, resource&& resource) { return nc::insert_resource(resources, std::move(resource)); }; + + // Deprecated: use nc::modify_resource + inline bool modify_control_protocol_resource(resources& resources, const id& id, std::function modifier, const web::json::value& notification_event = web::json::value::null()) { return nc::modify_resource(resources, id, modifier, notification_event); }; + + // Deprecated: use nc::erase_resource + inline resources::size_type erase_control_protocol_resource(resources& resources, const id& id) { return nc::erase_resource(resources, id); }; + + // Deprecated: use nc::find_resource + inline resources::const_iterator find_control_protocol_resource(resources& resources, type type, const id& id) { return nc::find_resource(resources, type, id); }; + + // Deprecated: use nc::method_parameters_contraints_validation + inline void method_parameters_contraints_validation(const web::json::value& arguments, const web::json::value& nc_method_descriptor, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor) { return nc::method_parameters_contraints_validation(arguments, nc_method_descriptor, get_control_protocol_datatype_descriptor); }; + + // Deprecated: use nc::insert_notification_events + inline void insert_notification_events(resources& resources, const api_version& version, const api_version& downgrade_version, const type& type, const web::json::value& pre, const web::json::value& post, const web::json::value& event) { return nc::insert_notification_events(resources, version, downgrade_version, type, pre, post, event); }; + + // Deprecated: use nc::get_property + inline web::json::value get_control_protocol_property(const resources& resources, nc_oid oid, const nc_property_id& property_id, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, slog::base_gate& gate) { return nc::get_property(resources, oid, property_id, get_control_protocol_class_descriptor, gate); }; } #endif \ No newline at end of file diff --git a/Development/nmos/control_protocol_ws_api.cpp b/Development/nmos/control_protocol_ws_api.cpp index ddc4b59db..855d09026 100644 --- a/Development/nmos/control_protocol_ws_api.cpp +++ b/Development/nmos/control_protocol_ws_api.cpp @@ -302,7 +302,7 @@ namespace nmos nc_method_result = nc::details::make_method_result_error({ nc_method_status::bad_oid }, ss.str()); } // accumulating up response - auto response = nc::make_control_protocol_response(handle, nc_method_result); + auto response = nc::make_response(handle, nc_method_result); web::json::push_back(responses, response); } @@ -310,7 +310,7 @@ namespace nmos // add command_response to the grain ready to transfer to the client in nmos::send_control_protocol_ws_messages_thread resources.modify(grain, [&](nmos::resource& grain) { - web::json::push_back(nmos::fields::message_grain_data(grain.data), nc::make_control_protocol_command_response(responses)); + web::json::push_back(nmos::fields::message_grain_data(grain.data), nc::make_command_response(responses)); grain.updated = strictly_increasing_update(resources); }); @@ -347,7 +347,7 @@ namespace nmos // add subscription_response to the grain ready to transfer to the client in nmos::send_control_protocol_ws_messages_thread resources.modify(grain, [&](nmos::resource& grain) { - web::json::push_back(nmos::fields::message_grain_data(grain.data), nc::make_control_protocol_subscription_response(valid_subscriptions)); + web::json::push_back(nmos::fields::message_grain_data(grain.data), nc::make_subscription_response(valid_subscriptions)); grain.updated = strictly_increasing_update(resources); }); @@ -369,7 +369,7 @@ namespace nmos resources.modify(grain, [&](nmos::resource& grain) { web::json::push_back(nmos::fields::message_grain_data(grain.data), - nc::make_control_protocol_error_message({ nc_method_status::bad_command_format }, utility::s2us(e.what()))); + nc::make_error_message({ nc_method_status::bad_command_format }, utility::s2us(e.what()))); grain.updated = strictly_increasing_update(resources); }); @@ -381,7 +381,7 @@ namespace nmos resources.modify(grain, [&](nmos::resource& grain) { web::json::push_back(nmos::fields::message_grain_data(grain.data), - nc::make_control_protocol_error_message({ nc_method_status::bad_command_format }, utility::s2us(std::string("Unexpected exception while handing control protocol command : ") + e.what()))); + nc::make_error_message({ nc_method_status::bad_command_format }, utility::s2us(std::string("Unexpected exception while handing control protocol command : ") + e.what()))); grain.updated = strictly_increasing_update(resources); }); @@ -393,7 +393,7 @@ namespace nmos resources.modify(grain, [&](nmos::resource& grain) { web::json::push_back(nmos::fields::message_grain_data(grain.data), - nc::make_control_protocol_error_message({ nc_method_status::bad_command_format }, U("Unexpected unknown exception while handing control protocol command"))); + nc::make_error_message({ nc_method_status::bad_command_format }, U("Unexpected unknown exception while handing control protocol command"))); grain.updated = strictly_increasing_update(resources); }); From 51cf6358a18f07814d9a3b7c6caabf114450b9e4 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 26 Sep 2025 16:58:54 +0100 Subject: [PATCH 249/250] Update comments and tidy-up --- Development/nmos/control_protocol_handlers.h | 3 ++- Development/nmos/control_protocol_methods.h | 7 ++++--- Development/nmos/control_protocol_state.cpp | 1 - Development/nmos/node_server.h | 3 ++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Development/nmos/control_protocol_handlers.h b/Development/nmos/control_protocol_handlers.h index 988059f03..18fa3824e 100644 --- a/Development/nmos/control_protocol_handlers.h +++ b/Development/nmos/control_protocol_handlers.h @@ -42,7 +42,7 @@ namespace nmos // callback to set monitor pending typedef std::function monitor_status_pending_handler; - // Receiver Monitor status callbacks + // Receiver & Sender Monitor status callbacks // these callbacks should not throw exceptions namespace nc { @@ -117,6 +117,7 @@ namespace nmos set_receiver_monitor_synchronization_source_id_handler make_set_receiver_monitor_synchronization_source_id_handler(resources& resources, experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); // NcSenderMonitor handlers + // Set sender monitor link status and link status message typedef std::function set_sender_monitor_link_status_handler; set_sender_monitor_link_status_handler make_set_sender_monitor_link_status_handler(resources& resources, experimental::control_protocol_state& control_protocol_state, slog::base_gate& gate); diff --git a/Development/nmos/control_protocol_methods.h b/Development/nmos/control_protocol_methods.h index b4f80ed54..5f0e51d15 100644 --- a/Development/nmos/control_protocol_methods.h +++ b/Development/nmos/control_protocol_methods.h @@ -45,11 +45,12 @@ namespace nmos // Get a single datatype descriptor web::json::value get_datatype(const web::json::value& arguments, bool is_deprecated, get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype, slog::base_gate& gate); - // NcReceiverMonitor methods implementation - // Gets the lost packet counters - web::json::value get_lost_packet_counters(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_packet_counters_handler get_lost_packet_counters, slog::base_gate& gate); + // NcReceiverMonitor method implementation // Gets the last packet counters web::json::value get_late_packet_counters(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_packet_counters_handler get_last_packet_counters, slog::base_gate& gate); + // NcReceiverMonitor & NcSenderMonitor methods implementation + // Gets the lost packet counters + web::json::value get_lost_packet_counters(nmos::resources& resources, const nmos::resource& resource, const web::json::value& arguments, bool is_deprecated, get_packet_counters_handler get_lost_packet_counters, slog::base_gate& gate); // Resets the packet counters and messages web::json::value reset_monitor(nmos::resources& resources, const nmos::resource& resource, const web::json::value&, bool is_deprecated, get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, control_protocol_property_changed_handler property_changed, reset_monitor_handler reset_monitor, slog::base_gate& gate); } diff --git a/Development/nmos/control_protocol_state.cpp b/Development/nmos/control_protocol_state.cpp index fe5ecca34..8e6b90762 100644 --- a/Development/nmos/control_protocol_state.cpp +++ b/Development/nmos/control_protocol_state.cpp @@ -417,7 +417,6 @@ namespace nmos to_methods_vector(nc::make_sender_monitor_methods(), { // link NcSenderMonitor method_ids with method functions - // TODO: implement actual GetTransmissionError and ResetCountersAndMessages function { nc_sender_monitor_get_transmission_error_counters_method_id, details::make_nc_get_lost_packet_counters_handler(get_lost_packet_counters)}, { nc_sender_monitor_reset_monitor_method_id, details::make_nc_reset_monitor_handler(get_control_protocol_class_descriptor, property_changed, reset_monitor)} }), diff --git a/Development/nmos/node_server.h b/Development/nmos/node_server.h index a586dd0cc..84597a3f8 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -28,7 +28,8 @@ namespace nmos // underlying implementation into the server instance for the NMOS Node struct node_implementation { - node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::create_validation_fingerprint_handler create_validation_fingerprint, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, create_device_model_object_handler create_device_model_object,nmos::control_protocol_connection_activation_handler monitor_connection_activated, nmos::get_packet_counters_handler get_lost_packet_counters, nmos::get_packet_counters_handler get_late_packet_counters, nmos::reset_monitor_handler reset_monitor) : load_server_certificates(std::move(load_server_certificates)) + node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::ocsp_response_handler get_ocsp_response, get_authorization_bearer_token_handler get_authorization_bearer_token, validate_authorization_handler validate_authorization, ws_validate_authorization_handler ws_validate_authorization, nmos::load_rsa_private_keys_handler load_rsa_private_keys, load_authorization_clients_handler load_authorization_clients, save_authorization_client_handler save_authorization_client, request_authorization_code_handler request_authorization_code, nmos::get_control_protocol_class_descriptor_handler get_control_protocol_class_descriptor, nmos::get_control_protocol_datatype_descriptor_handler get_control_protocol_datatype_descriptor, nmos::get_control_protocol_method_descriptor_handler get_control_protocol_method_descriptor, nmos::control_protocol_property_changed_handler control_protocol_property_changed, nmos::create_validation_fingerprint_handler create_validation_fingerprint, nmos::validate_validation_fingerprint_handler validate_validation_fingerprint, nmos::get_read_only_modification_allow_list_handler get_read_only_modification_allow_list, remove_device_model_object_handler remove_device_model_object, create_device_model_object_handler create_device_model_object,nmos::control_protocol_connection_activation_handler monitor_connection_activated, nmos::get_packet_counters_handler get_lost_packet_counters, nmos::get_packet_counters_handler get_late_packet_counters, nmos::reset_monitor_handler reset_monitor) + : load_server_certificates(std::move(load_server_certificates)) , load_dh_param(std::move(load_dh_param)) , load_ca_certificates(std::move(load_ca_certificates)) , system_changed(std::move(system_changed)) From ae577c3873729d869dab49b31030f54c43b3e62c Mon Sep 17 00:00:00 2001 From: lo-simon Date: Fri, 26 Sep 2025 17:02:42 +0100 Subject: [PATCH 250/250] Tidy-up and remove unnecessary make_callback's parameters --- .../nmos-cpp-node/node_implementation.cpp | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index e0349f680..9661a1093 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1275,12 +1275,12 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // making an object rebuildable allows read only properties to be modified by the Configuration API in Rebuild mode nmos::make_rebuildable(example_control); - const auto receiver_block_oid = ++oid; - auto receiver_block = nmos::make_block(receiver_block_oid, nmos::root_block_oid, U("receivers"), U("Receiver Monitors"), U("Receiver Monitors")); + const auto receivers_block_oid = ++oid; + auto receivers_block = nmos::make_block(receivers_block_oid, nmos::root_block_oid, U("receivers"), U("Receiver Monitors"), U("Receiver Monitors")); // making a block rebuildable allows block members to be added or removed by the Configuration API in Rebuild mode - nmos::make_rebuildable(receiver_block); + nmos::make_rebuildable(receivers_block); // restrict the allowed classes for members of this block - nmos::set_block_allowed_member_classes(receiver_block, {nmos::nc_receiver_monitor_class_id}); + nmos::set_block_allowed_member_classes(receivers_block, {nmos::nc_receiver_monitor_class_id}); // example receiver-monitor(s) { @@ -1294,11 +1294,11 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr utility::ostringstream_t role; role << U("receiver-monitor-") << ++count; const auto& receiver = nmos::find_resource(model.node_resources, receiver_id); - auto receiver_monitor = nmos::make_receiver_monitor(++oid, true, receiver_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, receiver_id}) } })); + auto receiver_monitor = nmos::make_receiver_monitor(++oid, true, receivers_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, receiver_id}) } })); // optionally indicate dependencies within the device model nmos::set_object_dependency_paths(receiver_monitor, {{U("root"), U("receivers")}}); - // add receiver-monitor to root-block - nmos::nc::push_back(receiver_block, receiver_monitor); + // add receiver-monitor to receivers-block + nmos::nc::push_back(receivers_block, receiver_monitor); } } } @@ -1326,8 +1326,8 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr // example temperature-sensor const auto temperature_sensor = make_temperature_sensor(++oid, nmos::root_block_oid, U("temperature-sensor"), U("Temperature Sensor"), U("Temperature Sensor block"), value::null(), value::null(), 0.0, U("Celsius")); - // add receiver monitor block - nmos::nc::push_back(root_block, receiver_block); + // add receivers-block to root-block + nmos::nc::push_back(root_block, receivers_block); // add temperature-sensor to root-block nmos::nc::push_back(root_block, temperature_sensor); // add example-control to root-block @@ -1943,7 +1943,7 @@ nmos::control_protocol_property_changed_handler make_node_implementation_control // Example Control Protocol WebSocket API Receiver Status Monitor callback to get network interface controller lost packet counters nmos::get_packet_counters_handler make_node_implementation_get_lost_packet_counters_handler() { - return [&]() + return []() { return boost::copy_range>(impl::nic_packet_counters | boost::adaptors::transformed([](const impl::nic_packet_counter& counter) { @@ -1955,7 +1955,7 @@ nmos::get_packet_counters_handler make_node_implementation_get_lost_packet_count // Example Control Protocol WebSocket API Receiver Status Monitor callback to get network interface controller late packet counters nmos::get_packet_counters_handler make_node_implementation_get_late_packet_counters_handler() { - return [&]() + return []() { return boost::copy_range>(impl::nic_packet_counters | boost::adaptors::transformed([](const impl::nic_packet_counter& counter) { @@ -1967,7 +1967,7 @@ nmos::get_packet_counters_handler make_node_implementation_get_late_packet_count // Example Control Protocol WebSocket API Receiver Status Monitor callback to reset network interface controller packet counters nmos::reset_monitor_handler make_node_implementation_reset_monitor_handler() { - return [&]() + return []() { for (auto& counter : impl::nic_packet_counters) { @@ -1981,9 +1981,9 @@ nmos::reset_monitor_handler make_node_implementation_reset_monitor_handler() // IS-14 Device Configuration callback // This function should generate a fingerprint that can be used for subsequent validation. -nmos::create_validation_fingerprint_handler make_create_validation_fingerprint_handler(slog::base_gate& gate) +nmos::create_validation_fingerprint_handler make_create_validation_fingerprint_handler() { - return [&gate](const nmos::resources& resources, const nmos::resource& resource) + return [](const nmos::resources& resources, const nmos::resource& resource) { return U("Sony nmos-cpp node"); }; @@ -1991,9 +1991,9 @@ nmos::create_validation_fingerprint_handler make_create_validation_fingerprint_h // IS-14 Device Configuration callback // This function called by a validate or restore and can be used to validate a validation fingerprint. Returning false will fail the validate or restore operation. -nmos::validate_validation_fingerprint_handler make_validate_validation_fingerprint_handler(slog::base_gate& gate) +nmos::validate_validation_fingerprint_handler make_validate_validation_fingerprint_handler() { - return [&gate](const nmos::resources& resources, const nmos::resource& resource, const utility::string_t& validation_fingerprint) + return [](const nmos::resources& resources, const nmos::resource& resource, const utility::string_t& validation_fingerprint) { return true; }; @@ -2020,9 +2020,9 @@ nmos::get_read_only_modification_allow_list_handler make_get_read_only_modificat // If this function returns true and validate is false then the object will be deleted. // If this function returns true/false or validate is true then the object will not be deleted. // If this function returns false an appropriate error will be passed to the calling client. -nmos::remove_device_model_object_handler make_remove_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate) +nmos::remove_device_model_object_handler make_remove_device_model_object_handler() { - return [&model, &gate](const nmos::resource& resource, const std::vector& role_path, bool validate) + return [](const nmos::resource& resource, const std::vector& role_path, bool validate) { // Perform application code functions here // resource - device model object about to be deleted @@ -2037,9 +2037,9 @@ nmos::remove_device_model_object_handler make_remove_device_model_object_handler // The returned object is then added to the Device Model // This example shows the creation of a receiver monitor resource // In the Device Model the receivers block that contains the monitors must be rebuildable -nmos::create_device_model_object_handler make_create_device_model_object_handler(nmos::node_model& model, slog::base_gate& gate) +nmos::create_device_model_object_handler make_create_device_model_object_handler(slog::base_gate& gate) { - return[&model, &gate](const nmos::nc_class_id& class_id, nmos::nc_oid oid, bool constant_oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, bool validate, const std::map& property_values) + return[&gate](const nmos::nc_class_id& class_id, nmos::nc_oid oid, bool constant_oid, nmos::nc_oid owner, const utility::string_t& role, const utility::string_t& user_label, const web::json::value& touchpoints, bool validate, const std::map& property_values) { if (touchpoints.size() != 1) { @@ -2208,11 +2208,11 @@ nmos::experimental::node_implementation make_node_implementation(nmos::node_mode .on_validate_channelmapping_output_map(make_node_implementation_map_validator()) // may be omitted if not required .on_channelmapping_activated(make_node_implementation_channelmapping_activation_handler(gate)) .on_control_protocol_property_changed(make_node_implementation_control_protocol_property_changed_handler(gate)) // may be omitted if IS-12 not required - .on_create_validation_fingerprint(make_create_validation_fingerprint_handler(gate)) - .on_validate_validation_fingerprint(make_validate_validation_fingerprint_handler(gate)) + .on_create_validation_fingerprint(make_create_validation_fingerprint_handler()) + .on_validate_validation_fingerprint(make_validate_validation_fingerprint_handler()) .on_get_read_only_modification_allow_list(make_get_read_only_modification_allow_list_handler(gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required - .on_remove_device_model_object(make_remove_device_model_object_handler(model, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required - .on_create_device_model_object(make_create_device_model_object_handler(model, gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required + .on_remove_device_model_object(make_remove_device_model_object_handler()) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required + .on_create_device_model_object(make_create_device_model_object_handler(gate)) // may be omitted if either IS-14 not required, or IS-14 Rebuild functionality not required .on_get_lost_packet_counters(make_node_implementation_get_lost_packet_counters_handler()) // may be omitted if IS-12/BCP-008-1 not required .on_get_late_packet_counters(make_node_implementation_get_late_packet_counters_handler()) // may be omitted if IS-12/BCP-008-1 not required .on_reset_monitor(make_node_implementation_reset_monitor_handler()); // may be omitted if IS-12/BCP-008-1 not required