From 71f13f0ab8c44ab3932fae882b0e6100e5676053 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Tue, 9 May 2023 16:43:33 +0100 Subject: [PATCH 001/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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/130] 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 {