diff --git a/Development/nmos-cpp-node/main.cpp b/Development/nmos-cpp-node/main.cpp index ae9d4380..dbd2a8f0 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 fa4293c0..67360c8c 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 f51b85cf..e650496a 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 bab1904f..a2127761 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 5ab2ccc3..60661b96 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 b95c26e4..54431fca 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 26948a8c..beb54823 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 7fb79a52..bbb68627 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 ccc9d1e3..babb2c04 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 95fdca05..a58b17e0 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