17constexpr const char* LOG_TAG =
"SubMgr";
19constexpr uint32_t MIN_ID = 1;
20constexpr uint32_t MAX_ID = 65535;
23template <
typename Map>
24uint32_t lowest_free_id(
const Map& taken) {
25 for (uint32_t
id = MIN_ID;
id <= MAX_ID; ++id) {
26 if (taken.count(
id) == 0)
return id;
37 std::unique_lock lock(mutex_);
38 registered_dapps_.clear();
39 dapp_subscriptions_.clear();
40 ran_function_subscribers_.clear();
41 subscription_ids_.clear();
42 subscription_id_reverse_.clear();
43 E3_LOG_DEBUG(LOG_TAG) <<
"Subscription manager destroyed";
47 std::unique_lock lock(mutex_);
48 sm_lifecycle_callback_ = std::move(callback);
56 std::unique_lock lock(mutex_);
60 const uint32_t assigned_id = lowest_free_id(registered_dapps_);
61 if (assigned_id == 0) {
62 E3_LOG_ERROR(LOG_TAG) <<
"No available dApp IDs (range " << MIN_ID
63 <<
".." << MAX_ID <<
")";
64 return {ErrorCode::INTERNAL_ERROR, 0};
70 registered_dapps_[assigned_id] = entry;
73 dapp_subscriptions_[assigned_id] = {};
75 E3_LOG_INFO(LOG_TAG) <<
"dApp registered successfully with ID " << assigned_id;
76 return {ErrorCode::SUCCESS, assigned_id};
80 std::unique_lock lock(mutex_);
82 auto it = registered_dapps_.find(dapp_id);
83 if (it == registered_dapps_.end()) {
84 E3_LOG_WARN(LOG_TAG) <<
"dApp " << dapp_id <<
" not found for unregistration";
85 return ErrorCode::DAPP_NOT_REGISTERED;
89 std::vector<std::pair<uint32_t, bool>> affected_ran_functions;
91 auto sub_it = dapp_subscriptions_.find(dapp_id);
92 if (sub_it != dapp_subscriptions_.end()) {
93 for (uint32_t ran_func : sub_it->second) {
95 auto& subscribers = ran_function_subscribers_[ran_func];
96 bool had_subscribers = !subscribers.empty();
99 subscribers.erase(dapp_id);
102 bool still_has_subscribers = !subscribers.empty();
103 if (had_subscribers && !still_has_subscribers) {
104 affected_ran_functions.emplace_back(ran_func,
false);
108 uint64_t sub_key = make_sub_key(dapp_id, ran_func);
109 auto sub_id_it = subscription_id_reverse_.find(sub_key);
110 if (sub_id_it != subscription_id_reverse_.end()) {
111 subscription_ids_.erase(sub_id_it->second);
112 subscription_id_reverse_.erase(sub_id_it);
114 subscription_details_.erase(sub_key);
116 dapp_subscriptions_.erase(sub_it);
120 registered_dapps_.erase(it);
122 size_t subscriptions_removed = affected_ran_functions.size();
123 E3_LOG_INFO(LOG_TAG) <<
"dApp " << dapp_id <<
" unregistered, "
124 << subscriptions_removed <<
" subscriptions removed";
128 for (
const auto& [ran_func, should_start] : affected_ran_functions) {
129 check_sm_lifecycle(ran_func,
true);
132 return ErrorCode::SUCCESS;
136 std::shared_lock lock(mutex_);
137 return registered_dapps_.count(dapp_id) > 0;
141 std::shared_lock lock(mutex_);
142 std::vector<uint32_t> result;
143 result.reserve(registered_dapps_.size());
144 for (
const auto& [
id, entry] : registered_dapps_) {
145 result.push_back(
id);
156 uint32_t ran_function_id,
157 const std::vector<uint32_t>& telemetry_ids,
158 const std::vector<uint32_t>& control_ids,
159 uint32_t periodicity_us) {
160 std::unique_lock lock(mutex_);
163 if (registered_dapps_.count(dapp_id) == 0) {
164 E3_LOG_ERROR(LOG_TAG) <<
"dApp " << dapp_id <<
" not registered, cannot add subscription";
165 return {ErrorCode::DAPP_NOT_REGISTERED, 0};
169 auto& dapp_subs = dapp_subscriptions_[dapp_id];
170 if (dapp_subs.count(ran_function_id) > 0) {
172 uint64_t sub_key = make_sub_key(dapp_id, ran_function_id);
173 uint32_t existing_sub_id = subscription_id_reverse_[sub_key];
174 E3_LOG_WARN(LOG_TAG) <<
"dApp " << dapp_id <<
" already subscribed to RAN function "
175 << ran_function_id <<
" (subscription_id=" << existing_sub_id <<
")";
176 return {ErrorCode::SUBSCRIPTION_EXISTS, existing_sub_id};
180 bool had_subscribers = !ran_function_subscribers_[ran_function_id].empty();
183 const uint32_t subscription_id = lowest_free_id(subscription_ids_);
184 if (subscription_id == 0) {
185 E3_LOG_ERROR(LOG_TAG) <<
"No available subscription IDs (range " << MIN_ID
186 <<
".." << MAX_ID <<
")";
187 return {ErrorCode::INTERNAL_ERROR, 0};
191 dapp_subs.insert(ran_function_id);
192 ran_function_subscribers_[ran_function_id].insert(dapp_id);
195 subscription_ids_[subscription_id] = {dapp_id, ran_function_id};
196 uint64_t sub_key = make_sub_key(dapp_id, ran_function_id);
197 subscription_id_reverse_[sub_key] = subscription_id;
204 subscription_details_[sub_key] = std::move(details);
206 E3_LOG_INFO(LOG_TAG) <<
"Subscription added: dApp " << dapp_id
207 <<
" -> RAN function " << ran_function_id
208 <<
" (subscription_id=" << subscription_id
209 <<
", periodicity_us=" << periodicity_us
210 <<
", telemetry_ids=" << telemetry_ids.size()
211 <<
", control_ids=" << control_ids.size() <<
")";
215 if (!had_subscribers) {
216 check_sm_lifecycle(ran_function_id,
false);
219 return {ErrorCode::SUCCESS, subscription_id};
223 std::unique_lock lock(mutex_);
225 auto dapp_it = dapp_subscriptions_.find(dapp_id);
226 if (dapp_it == dapp_subscriptions_.end()) {
227 return ErrorCode::SUBSCRIPTION_NOT_FOUND;
230 auto& dapp_subs = dapp_it->second;
231 if (dapp_subs.erase(ran_function_id) == 0) {
232 E3_LOG_WARN(LOG_TAG) <<
"Subscription not found for dApp " << dapp_id
233 <<
" -> RAN function " << ran_function_id;
234 return ErrorCode::SUBSCRIPTION_NOT_FOUND;
238 auto& subscribers = ran_function_subscribers_[ran_function_id];
239 bool had_subscribers = !subscribers.empty();
240 subscribers.erase(dapp_id);
241 bool still_has_subscribers = !subscribers.empty();
244 uint64_t sub_key = make_sub_key(dapp_id, ran_function_id);
245 auto sub_id_it = subscription_id_reverse_.find(sub_key);
246 if (sub_id_it != subscription_id_reverse_.end()) {
247 subscription_ids_.erase(sub_id_it->second);
248 subscription_id_reverse_.erase(sub_id_it);
250 subscription_details_.erase(sub_key);
252 E3_LOG_INFO(LOG_TAG) <<
"Subscription removed: dApp " << dapp_id
253 <<
" -> RAN function " << ran_function_id;
257 if (had_subscribers && !still_has_subscribers) {
258 check_sm_lifecycle(ran_function_id,
true);
261 return ErrorCode::SUCCESS;
265 std::unique_lock lock(mutex_);
268 auto sub_it = subscription_ids_.find(subscription_id);
269 if (sub_it == subscription_ids_.end()) {
270 E3_LOG_WARN(LOG_TAG) <<
"Subscription ID " << subscription_id <<
" not found";
271 return ErrorCode::SUBSCRIPTION_NOT_FOUND;
274 auto [sub_dapp_id, ran_function_id] = sub_it->second;
277 if (sub_dapp_id != dapp_id) {
278 E3_LOG_WARN(LOG_TAG) <<
"Subscription ID " << subscription_id
279 <<
" does not belong to dApp " << dapp_id;
280 return ErrorCode::SUBSCRIPTION_NOT_FOUND;
284 auto dapp_it = dapp_subscriptions_.find(dapp_id);
285 if (dapp_it != dapp_subscriptions_.end()) {
286 dapp_it->second.erase(ran_function_id);
290 auto& subscribers = ran_function_subscribers_[ran_function_id];
291 bool had_subscribers = !subscribers.empty();
292 subscribers.erase(dapp_id);
293 bool still_has_subscribers = !subscribers.empty();
296 uint64_t sub_key = make_sub_key(dapp_id, ran_function_id);
297 subscription_id_reverse_.erase(sub_key);
298 subscription_ids_.erase(sub_it);
299 subscription_details_.erase(sub_key);
301 E3_LOG_INFO(LOG_TAG) <<
"Subscription " << subscription_id <<
" removed: dApp " << dapp_id
302 <<
" -> RAN function " << ran_function_id;
306 if (had_subscribers && !still_has_subscribers) {
307 check_sm_lifecycle(ran_function_id,
true);
310 return ErrorCode::SUCCESS;
314 std::shared_lock lock(mutex_);
316 auto it = dapp_subscriptions_.find(dapp_id);
317 if (it == dapp_subscriptions_.end()) {
320 return it->second.count(ran_function_id) > 0;
324 std::shared_lock lock(mutex_);
326 auto it = dapp_subscriptions_.find(dapp_id);
327 if (it == dapp_subscriptions_.end()) {
330 return std::vector<uint32_t>(it->second.begin(), it->second.end());
334 std::shared_lock lock(mutex_);
336 auto it = ran_function_subscribers_.find(ran_function_id);
337 if (it == ran_function_subscribers_.end()) {
340 return std::vector<uint32_t>(it->second.begin(), it->second.end());
344 uint32_t dapp_id, uint32_t ran_function_id)
const {
345 std::shared_lock lock(mutex_);
347 auto it = subscription_details_.find(make_sub_key(dapp_id, ran_function_id));
348 if (it == subscription_details_.end()) {
355 std::shared_lock lock(mutex_);
357 auto it = ran_function_subscribers_.find(ran_function_id);
358 if (it == ran_function_subscribers_.end()) {
361 return it->second.size();
369 std::shared_lock lock(mutex_);
371 std::vector<uint32_t> result;
372 for (
const auto& [ran_func, subscribers] : ran_function_subscribers_) {
373 if (!subscribers.empty()) {
374 result.push_back(ran_func);
381 std::shared_lock lock(mutex_);
383 auto it = ran_function_subscribers_.find(ran_function_id);
384 return it != ran_function_subscribers_.end() && !it->second.empty();
392 std::shared_lock lock(mutex_);
393 return registered_dapps_.size();
397 std::shared_lock lock(mutex_);
400 for (
const auto& [dapp_id, subs] : dapp_subscriptions_) {
401 count += subs.size();
407 std::unique_lock lock(mutex_);
408 registered_dapps_.clear();
409 dapp_subscriptions_.clear();
410 ran_function_subscribers_.clear();
411 subscription_ids_.clear();
412 subscription_id_reverse_.clear();
413 subscription_details_.clear();
414 E3_LOG_INFO(LOG_TAG) <<
"All registrations and subscriptions cleared";
421void SubscriptionManager::check_sm_lifecycle(uint32_t ran_function_id,
bool had_subscribers) {
422 if (!sm_lifecycle_callback_) {
429 if (!had_subscribers && has_subscribers_now) {
431 E3_LOG_INFO(LOG_TAG) <<
"Starting SM for RAN function " << ran_function_id
432 <<
" (first subscriber)";
433 sm_lifecycle_callback_(ran_function_id,
true);
434 }
else if (had_subscribers && !has_subscribers_now) {
436 E3_LOG_INFO(LOG_TAG) <<
"Stopping SM for RAN function " << ran_function_id
437 <<
" (no remaining subscribers)";
438 sm_lifecycle_callback_(ran_function_id,
false);
std::vector< uint32_t > get_active_ran_functions() const
Get all RAN functions that have at least one subscriber.
std::vector< uint32_t > get_registered_dapps() const
Get list of registered dApp IDs.
ErrorCode remove_subscription(uint32_t dapp_id, uint32_t ran_function_id)
Remove a subscription between dApp and RAN function.
bool is_subscribed(uint32_t dapp_id, uint32_t ran_function_id) const
Check if dApp is subscribed to a RAN function.
void set_sm_lifecycle_callback(SmLifecycleCallback callback)
Set callback for SM lifecycle events.
ErrorCode unregister_dapp(uint32_t dapp_id)
Unregister a dApp and clean up all its subscriptions.
std::pair< ErrorCode, uint32_t > register_dapp()
Register a dApp from E3 Setup Request.
size_t get_subscriber_count(uint32_t ran_function_id) const
Get count of subscribers for a RAN function.
const SubscriptionDetails * get_subscription_details(uint32_t dapp_id, uint32_t ran_function_id) const
Get subscription details for a specific dApp + RAN function pair.
bool has_subscribers(uint32_t ran_function_id) const
Check if any dApp is subscribed to a RAN function.
~SubscriptionManager()
Destructor.
void clear()
Clear all registrations and subscriptions.
std::vector< uint32_t > get_dapp_subscriptions(uint32_t dapp_id) const
Get all RAN functions a dApp is subscribed to.
bool is_dapp_registered(uint32_t dapp_id) const
Check if a dApp is registered.
SubscriptionManager()
Construct a new Subscription Manager.
std::vector< uint32_t > get_subscribed_dapps(uint32_t ran_function_id) const
Get all dApps subscribed to a RAN function.
size_t subscription_count() const
Get total number of active subscriptions.
ErrorCode remove_subscription_by_id(uint32_t dapp_id, uint32_t subscription_id)
Remove a subscription by its ID.
std::pair< ErrorCode, uint32_t > add_subscription(uint32_t dapp_id, uint32_t ran_function_id, const std::vector< uint32_t > &telemetry_ids={}, const std::vector< uint32_t > &control_ids={}, uint32_t periodicity_us=0)
Add a subscription between dApp and RAN function.
size_t dapp_count() const
Get total number of registered dApps.
Logging facility for libe3.
#define E3_LOG_WARN(component)
#define E3_LOG_DEBUG(component)
#define E3_LOG_INFO(component)
#define E3_LOG_ERROR(component)
ErrorCode
Error codes returned by libe3 operations Use shared error-code list (see libe3/error_codes....
std::function< void(uint32_t ran_function_id, bool should_start)> SmLifecycleCallback
Callback type for SM lifecycle events.
Timestamp registered_time
Monotonic timestamp of when the dApp registered.
uint32_t dapp_identifier
Assigned dApp identifier (1–100).
Per-subscription metadata stored by the SubscriptionManager.
uint32_t periodicity_us
0 = use SM default
std::vector< uint32_t > control_ids
std::vector< uint32_t > telemetry_ids
E3 Subscription Manager for tracking dApp registrations.