@@ -174,37 +174,37 @@ namespace nmos
174174 if (datatype_constraints.is_null ())
175175 {
176176 auto primitive_validation = [](const nc_name& name, const web::json::value& value)
177+ {
178+ auto is_int16 = [](int32_t value)
179+ {
180+ return value >= (std::numeric_limits<int16_t >::min)()
181+ && value <= (std::numeric_limits<int16_t >::max)();
182+ };
183+ auto is_uint16 = [](uint32_t value)
177184 {
178- auto is_int16 = [](int32_t value)
179- {
180- return value >= (std::numeric_limits<int16_t >::min)()
181- && value <= (std::numeric_limits<int16_t >::max)();
182- };
183- auto is_uint16 = [](uint32_t value)
184- {
185- return value >= (std::numeric_limits<uint16_t >::min)()
186- && value <= (std::numeric_limits<uint16_t >::max)();
187- };
188- auto is_float32 = [](double value)
189- {
190- return value >= (std::numeric_limits<float_t >::lowest)()
191- && value <= (std::numeric_limits<float_t >::max)();
192- };
193-
194- if (U (" NcBoolean" ) == name) { return value.is_boolean (); }
195- if (U (" NcInt16" ) == name && value.is_number ()) { return is_int16 (value.as_number ().to_int32 ()); }
196- if (U (" NcInt32" ) == name && value.is_number ()) { return value.as_number ().is_int32 (); }
197- if (U (" NcInt64" ) == name && value.is_number ()) { return value.as_number ().is_int64 (); }
198- if (U (" NcUint16" ) == name && value.is_number ()) { return is_uint16 (value.as_number ().to_uint32 ()); }
199- if (U (" NcUint32" ) == name && value.is_number ()) { return value.as_number ().is_uint32 (); }
200- if (U (" NcUint64" ) == name && value.is_number ()) { return value.as_number ().is_uint64 (); }
201- if (U (" NcFloat32" ) == name && value.is_number ()) { return is_float32 (value.as_number ().to_double ()); }
202- if (U (" NcFloat64" ) == name && value.is_number ()) { return !value.as_number ().is_integral (); }
203- if (U (" NcString" ) == name) { return value.is_string (); }
204-
205- // invalid primitive type
206- return false ;
185+ return value >= (std::numeric_limits<uint16_t >::min)()
186+ && value <= (std::numeric_limits<uint16_t >::max)();
207187 };
188+ auto is_float32 = [](double value)
189+ {
190+ return value >= (std::numeric_limits<float_t >::lowest)()
191+ && value <= (std::numeric_limits<float_t >::max)();
192+ };
193+
194+ if (U (" NcBoolean" ) == name) { return value.is_boolean (); }
195+ if (U (" NcInt16" ) == name && value.is_number ()) { return is_int16 (value.as_number ().to_int32 ()); }
196+ if (U (" NcInt32" ) == name && value.is_number ()) { return value.as_number ().is_int32 (); }
197+ if (U (" NcInt64" ) == name && value.is_number ()) { return value.as_number ().is_int64 (); }
198+ if (U (" NcUint16" ) == name && value.is_number ()) { return is_uint16 (value.as_number ().to_uint32 ()); }
199+ if (U (" NcUint32" ) == name && value.is_number ()) { return value.as_number ().is_uint32 (); }
200+ if (U (" NcUint64" ) == name && value.is_number ()) { return value.as_number ().is_uint64 (); }
201+ if (U (" NcFloat32" ) == name && value.is_number ()) { return is_float32 (value.as_number ().to_double ()); }
202+ if (U (" NcFloat64" ) == name && value.is_number ()) { return !value.as_number ().is_integral (); }
203+ if (U (" NcString" ) == name) { return value.is_string (); }
204+
205+ // invalid primitive type
206+ return false ;
207+ };
208208
209209 // do primitive type constraints validation
210210 const auto & name = nmos::fields::nc::name (params.datatype_descriptor );
@@ -551,13 +551,31 @@ namespace nmos
551551 web::json::push_back (parent[nmos::fields::nc::members],
552552 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)));
553553
554+ // add to temporary storage until they are moved to the model resources
554555 nc_block_resource.resources .push_back (resource);
555556 }
556557
558+ // insert a control protocol resource
559+ std::pair<resources::iterator, bool > insert_control_protocol_resource (resources& resources, resource&& resource)
560+ {
561+ // set the creation and update timestamps, before inserting the resource
562+ resource.updated = resource.created = nmos::strictly_increasing_update (resources);
563+
564+ auto result = resources.insert (std::move (resource));
565+ // replacement of a deleted or expired resource is also allowed
566+ // (currently, with no further checks on api_version, type, etc.)
567+ if (!result.second && !result.first ->has_data ())
568+ {
569+ // if the insertion was banned, resource has not been moved from
570+ result.second = resources.replace (result.first , std::move (resource));
571+ }
572+ return result;
573+ }
574+
557575 // modify a control protocol resource, and insert notification event to all subscriptions
558576 bool modify_control_protocol_resource (resources& resources, const id& id, std::function<void (resource&)> modifier, const web::json::value& notification_event)
559577 {
560- // note, model write lock should aleady be applied by the outer function, so access to control_protocol_resources is OK...
578+ // note, model write lock should already be applied by the outer function, so access to control_protocol_resources is OK...
561579
562580 auto found = resources.find (id);
563581 if (resources.end () == found || !found->has_data ()) return false ;
@@ -589,7 +607,6 @@ namespace nmos
589607 if (result)
590608 {
591609 auto & modified = *found;
592-
593610 insert_notification_events (resources, modified.version , modified.downgrade_version , modified.type , pre , modified.data , notification_event);
594611 }
595612
@@ -601,6 +618,20 @@ namespace nmos
601618 return result;
602619 }
603620
621+ // erase a control protocol resource
622+ resources::size_type erase_control_protocol_resource (resources& resources, const id& id)
623+ {
624+ // hmm, may be also erasing all it's member blocks?
625+ resources::size_type count = 0 ;
626+ auto found = resources.find (id);
627+ if (resources.end () != found && found->has_data ())
628+ {
629+ resources.erase (found);
630+ ++count;
631+ }
632+ return count;
633+ }
634+
604635 // find the control protocol resource which is assoicated with the given IS-04/IS-05/IS-08 resource id
605636 resources::const_iterator find_control_protocol_resource (resources& resources, type type, const id& resource_id)
606637 {
@@ -613,8 +644,7 @@ namespace nmos
613644 auto found_tp = std::find_if (tps.begin (), tps.end (), [resource_id](const web::json::value& touchpoint)
614645 {
615646 auto & resource = nmos::fields::nc::resource (touchpoint);
616- return (resource_id == nmos::fields::nc::id (resource).as_string ()
617- && nmos::ncp_nmos_resource_types::receiver.name == nmos::fields::nc::resource_type (resource));
647+ return (resource_id == nmos::fields::nc::id (resource).as_string ());
618648 });
619649 return (tps.end () != found_tp);
620650 }
@@ -638,4 +668,47 @@ namespace nmos
638668 details::method_parameter_constraints_validation (arguments.at (name), constraints, { nmos::details::get_datatype_descriptor (type_name, get_control_protocol_datatype_descriptor), get_control_protocol_datatype_descriptor });
639669 }
640670 }
671+
672+ // 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
673+ // this is used for the IS-12 propertry changed event
674+ 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)
675+ {
676+ using web::json::value;
677+
678+ if (pre == post ) return ;
679+
680+ auto & by_type = resources.get <tags::type>();
681+ const auto subscriptions = by_type.equal_range (details::has_data (nmos::types::subscription));
682+
683+ for (auto it = subscriptions.first ; subscriptions.second != it; ++it)
684+ {
685+ // for each subscription
686+ const auto & subscription = *it;
687+
688+ // check whether the resource_path matches the resource type and the query parameters match either the "pre" or "post" resource
689+
690+ const auto resource_path = nmos::fields::resource_path (subscription.data );
691+ const resource_query match (subscription.version , resource_path, nmos::fields::params (subscription.data ));
692+
693+ const bool pre_match = match (version, downgrade_version, type, pre , resources);
694+ const bool post_match = match (version, downgrade_version, type, post , resources);
695+
696+ if (!pre_match && !post_match) continue ;
697+
698+ // add the event to the grain for each websocket connection to this subscription
699+
700+ for (const auto & id : subscription.sub_resources )
701+ {
702+ auto grain = find_resource (resources, { id, nmos::types::grain });
703+ if (resources.end () == grain) continue ; // check websocket connection is still open
704+
705+ resources.modify (grain, [&resources, &event](nmos::resource& grain)
706+ {
707+ auto & events = nmos::fields::message_grain_data (grain.data );
708+ web::json::push_back (events, event);
709+ grain.updated = strictly_increasing_update (resources);
710+ });
711+ }
712+ }
713+ }
641714}
0 commit comments