Skip to content
219 changes: 210 additions & 9 deletions Development/nmos-cpp-node/node_implementation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,143 @@ namespace impl
nmos::nc::counter late_packet_counter;
};
std::vector<nic_packet_counter> nic_packet_counters;

// Example of sender control class descriptors. Used below for setting up a sender control class
void make_sender_control_descriptors(nmos::experimental::control_protocol_state & control_protocol_state, nmos::nc_class_id snd_class_id)
{
using web::json::value;
using web::json::value_of;

// Datatype description for an object within an object

auto make_struct_datatype = [](const std::vector<std::tuple<utility::string_t,utility::string_t,utility::string_t, bool>>& obj_desc,
const utility::string_t& descriptor, const utility::string_t& type_name) {
using web::json::value;
auto fields = value::array();
for ( auto obj : obj_desc ) {
auto data = nmos::nc::details::make_field_descriptor(std::get<0>(obj), std::get<1>(obj), std::get<2>(obj), true, std::get<3>(obj), value::null());
web::json::push_back(fields, std::move(data));
}
auto ret = nmos::nc::details::make_datatype_descriptor_struct(descriptor, type_name, fields, value::null());
return ret;
};
std::vector<std::tuple<utility::string_t,utility::string_t,utility::string_t, bool>> subscription_struct_fields {
// description, name, type_name, is_sequence
{U("active"), U("active"), U("NcBoolean"), false},
{U("receiver_id"), U("receiver_id"), U("NcUuid"), false}
};
auto subscription_data_type = make_struct_datatype(subscription_struct_fields, U("SubscriptionType"), U("SubscriptionType"));
control_protocol_state.insert(std::move(nmos::experimental::datatype_descriptor{ subscription_data_type }));
std::vector<std::tuple<utility::string_t, utility::string_t, utility::string_t, bool>> key_value_struct_fields{
// description, name, type_name, is_sequence
{U("key"), U("key"), U("NcString"), false},
{U("value"), U("value"), U("NcString"), true}
};
auto key_value_data_type = make_struct_datatype(key_value_struct_fields, U("KeyValueStruct"), U("KeyValueStruct"));
control_protocol_state.insert(std::move(nmos::experimental::datatype_descriptor{ key_value_data_type }));

const web::json::field_as_value caps_property{ U("caps") };
const web::json::field_as_string description_property{ U("description") };
const web::json::field_as_string device_id_property{ U("device_id") };
const web::json::field_as_string flow_id_property{ U("flow_id") };
const web::json::field_as_string uuid_property{ U("resource_id") };
const web::json::field_as_array interface_bindings_property{ U("interface_bindings") };
const web::json::field_as_string label_property{ U("label") };
const web::json::field_as_string manifest_href_property{ U("manifest_href") };
const web::json::field_as_array subscription_property{ U("subscription") };
const web::json::field_as_value tags_property{ U("tags") };
const web::json::field_as_string transport_property{ U("transport") };
const web::json::field<nmos::tai> version_property{ U("version") };

// Define property descriptors
std::vector<web::json::value> snd_control_property_descriptors = {
// Property descriptor for required fields
nmos::experimental::make_control_class_property_descriptor(U("description"), { 3, 1 }, description_property, U("NcString"), true, false, false, false, web::json::value::null()),
nmos::experimental::make_control_class_property_descriptor(U("resource_id"), { 3, 2 }, uuid_property, U("NcUuid"), true, false, false, false, web::json::value::null()),
nmos::experimental::make_control_class_property_descriptor(U("label"), { 3, 3 }, label_property, U("NcString"), true, false, false, false, web::json::value::null()),
nmos::experimental::make_control_class_property_descriptor(U("tags"), { 3, 4 }, tags_property, U("KeyValueStruct"), true, false, true, false, web::json::value::null()),
nmos::experimental::make_control_class_property_descriptor(U("version"), { 3, 5 }, version_property, U("NcVersionCode"), true, false, false, false, web::json::value::null()),

// Property descriptor for optional fields
nmos::experimental::make_control_class_property_descriptor(U("caps"), { 3, 6 }, caps_property, U("KeyValueStruct"), true, true, true, false, web::json::value::null()),
nmos::experimental::make_control_class_property_descriptor(U("description"), { 3, 7 }, device_id_property, U("NcUuid"), true, true, false, false, web::json::value::null()),
nmos::experimental::make_control_class_property_descriptor(U("flow_id"), { 3, 8 }, flow_id_property, U("NcUuid"), true, true, false, false, web::json::value::null()),
nmos::experimental::make_control_class_property_descriptor(U("interface_bindings"), { 3, 9 }, interface_bindings_property, U("NcString"), true, false, true, false, web::json::value::null()),
nmos::experimental::make_control_class_property_descriptor(U("manifest_href"), { 3, 10 }, manifest_href_property, U("NcUri"), true, true, false, false, web::json::value::null()),
nmos::experimental::make_control_class_property_descriptor(U("subscription"), { 3, 11 }, subscription_property, U("SubscriptionType"), true, true, false, false, web::json::value::null()),
nmos::experimental::make_control_class_property_descriptor(U("transport"), { 3, 12 }, transport_property, U("NcString"), true, true, false, false, web::json::value::null())
};
// method and event descriptors are defined by defaults in the function prototype, so only need to pass the property descriptors
utility::string_t descriptor_description = utility::string_t(U("SenderControl")) + utility::string_t(U("Sender control class descriptor"));
auto sender_control_class_descriptor =
nmos::experimental::make_control_class_descriptor(descriptor_description,
snd_class_id, U("SenderControl"),
snd_control_property_descriptors);

// Insert class descriptor into the nmos-cpp framework
control_protocol_state.insert(std::move(sender_control_class_descriptor));
}
// Example of an sender control class
static nmos::control_protocol_resource make_sender_control(const nmos::nc_class_id& snd_class_id,
const web::json::value& sender_data,
const nmos::nc_oid& oid,
const utility::string_t& role,
const nmos::nc_oid& parent_oid,
const web::json::value& touchpoints)
{
using web::json::value;
using web::json::value_of;
auto make_key_value_array = [](const value& object)
{
auto key_value_array = value::array();

for (auto iter = object.as_object().begin(); iter != object.as_object().end(); iter++)
{
web::json::push_back(key_value_array, web::json::value_of({ { U("key"), (*iter).first}, {U("value"), (*iter).second } }));
}
return key_value_array;
};

const web::json::field_as_array caps_property{ U("caps") };
const web::json::field_as_string description_property{ U("description") };
const web::json::field_as_string device_id_property{ U("device_id") };
const web::json::field_as_string flow_id_property{ U("flow_id") };
const web::json::field_as_string uuid_property{ U("resource_id") };
const web::json::field_as_array interface_bindings_property{ U("interface_bindings") };
const web::json::field_as_string label_property{ U("label") };
const web::json::field_as_string manifest_href_property{ U("manifest_href") };
const web::json::field_as_array subscription_property{ U("subscription") };
const web::json::field_as_value tags_property{ U("tags") };
const web::json::field_as_string transport_property{ U("transport") };
const web::json::field<nmos::tai> version_property{ U("version") };

// define a function for instantiating object instances of the class
auto data = nmos::nc::details::make_worker(snd_class_id, oid,
true,
parent_oid,
role,
web::json::value(role),
U("Sender resource data"),
touchpoints,
web::json::value::null(),
true);

// Required
data[description_property] = sender_data.at(U("description"));
data[uuid_property] = sender_data.at(U("id"));
data[label_property] = sender_data.at(U("label"));
data[tags_property] = make_key_value_array(sender_data.at(U("tags")));
data[version_property] = sender_data.at(U("version"));
// Optional
data[caps_property] = sender_data.has_field(U("caps")) ? make_key_value_array(sender_data.at(U("caps"))) : web::json::value::array();
data[device_id_property] = sender_data.has_field(U("device_id")) ? sender_data.at(U("device_id")) : web::json::value::null();
data[flow_id_property] = sender_data.has_field(U("flow_id")) ? sender_data.at(U("flow_id")) : web::json::value::null();
data[interface_bindings_property] = sender_data.has_field(U("interface_bindings")) ? sender_data.at(U("interface_bindings")) : web::json::value::array();
data[manifest_href_property] = sender_data.has_field(U("manifest_href")) ? sender_data.at(U("manifest_href")) : web::json::value::null();
data[subscription_property] = sender_data.has_field(U("subscription")) ? sender_data.at(U("subscription")) : web::json::value::null();
data[transport_property] = sender_data.has_field(U("transport")) ? sender_data.at(U("transport")) : web::json::value::null();
return nmos::control_protocol_resource{ nmos::is12_versions::v1_0, nmos::types::nc_worker, std::move(data), true };
}
}

// forward declarations for node_implementation_thread
Expand Down Expand Up @@ -1212,6 +1349,10 @@ 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 audio sender control descriptors
auto sender_control_class_id = nmos::nc::make_class_id(nmos::nc_worker_class_id, 0, { 5 });
impl::make_sender_control_descriptors(control_protocol_state, sender_control_class_id);

// example root block
auto root_block = nmos::make_root_block();

Expand Down Expand Up @@ -1275,12 +1416,12 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr
// making an object rebuildable allows read only properties to be modified by the Configuration API in Rebuild mode
nmos::make_rebuildable(example_control);

const auto receivers_block_oid = ++oid;
auto receivers_block = nmos::make_block(receivers_block_oid, nmos::root_block_oid, U("receivers"), U("Receiver Monitors"), U("Receiver Monitors"));
const auto receiver_monitors_block_oid = ++oid;
auto receiver_monitors_block = nmos::make_block(receiver_monitors_block_oid, nmos::root_block_oid, U("receiver-monitors"), U("Receiver Monitors"), U("Receiver Monitors"));
// making a block rebuildable allows block members to be added or removed by the Configuration API in Rebuild mode
nmos::make_rebuildable(receivers_block);
nmos::make_rebuildable(receiver_monitors_block);
// restrict the allowed classes for members of this block
nmos::set_block_allowed_member_classes(receivers_block, {nmos::nc_receiver_monitor_class_id});
nmos::set_block_allowed_member_classes(receiver_monitors_block, {nmos::nc_receiver_monitor_class_id});

// example receiver-monitor(s)
{
Expand All @@ -1294,11 +1435,11 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr
utility::ostringstream_t role;
role << U("receiver-monitor-") << ++count;
const auto receiver = nmos::find_resource(model.node_resources, receiver_id);
auto receiver_monitor = nmos::make_receiver_monitor(++oid, true, receivers_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, receiver_id}) } }));
auto receiver_monitor = nmos::make_receiver_monitor(++oid, true, receiver_monitors_block_oid, role.str(), nmos::fields::label(receiver->data), nmos::fields::description(receiver->data), value_of({ { nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::receiver, receiver_id}) } }));
// optionally indicate dependencies within the device model
nmos::set_object_dependency_paths(receiver_monitor, {{U("root"), U("receivers")}});
nmos::set_object_dependency_paths(receiver_monitor, {{U("root"), U("receiver-monitors")}});
// add receiver-monitor to receivers-block
nmos::nc::push_back(receivers_block, receiver_monitor);
nmos::nc::push_back(receiver_monitors_block, receiver_monitor);
}
}
}
Expand All @@ -1323,11 +1464,71 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr
}
}

const auto sender_monitors_block_oid = ++oid;
auto sender_monitors_block = nmos::make_block(sender_monitors_block_oid, nmos::root_block_oid, U("sender-monitors"), U("Sender Monitors"), U("Sender Monitors"));
// making a block rebuildable allows block members to be added or removed by the Configuration API in Rebuild mode
nmos::make_rebuildable(sender_monitors_block);
// restrict the allowed classes for members of this block
nmos::set_block_allowed_member_classes(sender_monitors_block, { nmos::nc_sender_monitor_class_id });

// example sender-monitor(s)
{
int count = 0;
for (int index = 0; index < how_many; ++index)
{
for (const auto& port : rtp_sender_ports)
{
const auto sender_id = impl::make_id(seed_id, nmos::types::sender, port, index);

utility::ostringstream_t role;
role << U("sender-monitor-") << ++count;
const auto& sender = nmos::find_resource(model.node_resources, sender_id);
auto sender_monitor = nmos::make_sender_monitor(++oid, true, sender_monitors_block_oid, role.str(), nmos::fields::label(sender->data), nmos::fields::description(sender->data), value_of({ { nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::sender, sender_id}) } }));
// optionally indicate dependencies within the device model
nmos::set_object_dependency_paths(sender_monitor, { {U("root"), U("sender-monitors")} });
// add sender-monitor to root-block
nmos::nc::push_back(sender_monitors_block, sender_monitor);
}
}
}

const auto sender_controls_block_oid = ++oid;
auto sender_controls_block = nmos::make_block(sender_controls_block_oid, nmos::root_block_oid, U("sender-controls"), U("Sender Controls"), U("Sender Controls"));

// example sender control(s)
{
int count = 0;
for (int index = 0; index < how_many; ++index)
{
for (const auto& port : rtp_receiver_ports)
{

const auto sender_id = impl::make_id(seed_id, nmos::types::sender, port, index);

utility::ostringstream_t role;
role << U("sender-control-") << ++count;
const auto sender = nmos::find_resource(model.node_resources, sender_id);
auto sender_control = impl::make_sender_control(sender_control_class_id,
sender->data,
++oid, role.str(),
sender_controls_block_oid,
value_of({ { nmos::nc::details::make_touchpoint_nmos({nmos::ncp_touchpoint_resource_types::sender, sender_id}) } }));
// add sender-control to sender-controls-block
nmos::nc::push_back(sender_controls_block, sender_control);
}
}
}


// example temperature-sensor
const auto temperature_sensor = make_temperature_sensor(++oid, nmos::root_block_oid, U("temperature-sensor"), U("Temperature Sensor"), U("Temperature Sensor block"), value::null(), value::null(), 0.0, U("Celsius"));

// add receivers-block to root-block
nmos::nc::push_back(root_block, receivers_block);
// add receiver-monitors-block to root-block
nmos::nc::push_back(root_block, receiver_monitors_block);
// add sender-monitors-block to root-block
nmos::nc::push_back(root_block, sender_monitors_block);
// add sender-controls-block to root-block
nmos::nc::push_back(root_block, sender_controls_block);
// add temperature-sensor to root-block
nmos::nc::push_back(root_block, temperature_sensor);
// add example-control to root-block
Expand Down