libe3 0.2.5
C++ library for E3AP RAN vendor integration
Loading...
Searching...
No Matches
subscription_manager.hpp
Go to the documentation of this file.
1/**
2 * @file subscription_manager.hpp
3 * @brief E3 Subscription Manager for tracking dApp registrations
4 *
5 * This module manages the associations between dApps and RAN functions.
6 * Ported from the original C implementation's e3_subscription_manager.
7 *
8 * SPDX-FileCopyrightText: Copyright (c) 2026 Northeastern University
9 * SPDX-License-Identifier: Apache-2.0
10 */
11
12#ifndef LIBE3_SUBSCRIPTION_MANAGER_HPP
13#define LIBE3_SUBSCRIPTION_MANAGER_HPP
14
15#include "types.hpp"
16#include <mutex>
17#include <shared_mutex>
18#include <utility>
19#include <vector>
20#include <unordered_map>
21#include <unordered_set>
22#include <functional>
23
24namespace libe3 {
25
26/**
27 * @brief Per-subscription metadata stored by the SubscriptionManager.
28 */
30 std::vector<uint32_t> telemetry_ids;
31 std::vector<uint32_t> control_ids;
32 uint32_t periodicity_us{0}; ///< 0 = use SM default
33};
34
35/**
36 * @brief Callback type for SM lifecycle events
37 *
38 * Called when a RAN function gains its first subscriber or loses
39 * its last subscriber. This allows the E3Agent to start/stop
40 * Service Models as needed.
41 *
42 * @param ran_function_id The RAN function ID
43 * @param should_start True if SM should start, false if it should stop
44 */
45using SmLifecycleCallback = std::function<void(uint32_t ran_function_id, bool should_start)>;
46
47/**
48 * @brief E3 Subscription Manager
49 *
50 * Thread-safe manager for dApp registrations and RAN function subscriptions.
51 * Handles:
52 * - dApp registration from E3 Setup requests
53 * - Subscription management from E3 Subscription requests
54 * - RAN function lifecycle coordination
55 *
56 * This class is ported from the original C e3_subscription_manager
57 * with modern C++ idioms for thread safety and memory management.
58 */
60public:
61 /**
62 * @brief Construct a new Subscription Manager
63 */
65
66 /**
67 * @brief Destructor
68 */
70
71 // Non-copyable, non-movable (contains mutex)
76
77 /**
78 * @brief Set callback for SM lifecycle events
79 */
81
82 // =========================================================================
83 // dApp Registration Management (E3 Setup flow)
84 // =========================================================================
85
86 /**
87 * @brief Register a dApp from E3 Setup Request
88 *
89 * The SubscriptionManager assigns a unique dApp ID automatically.
90 *
91 * @return std::pair containing:
92 * - ErrorCode::SUCCESS on success, or error code on failure
93 * - The assigned dApp ID (valid only if ErrorCode::SUCCESS)
94 * @return ErrorCode::INTERNAL_ERROR if no IDs available (max 101 dApps)
95 */
96 std::pair<ErrorCode, uint32_t> register_dapp();
97
98 /**
99 * @brief Unregister a dApp and clean up all its subscriptions
100 *
101 * @param dapp_id dApp identifier
102 * @return ErrorCode::SUCCESS on success
103 * @return ErrorCode::DAPP_NOT_REGISTERED if dApp not found
104 */
105 ErrorCode unregister_dapp(uint32_t dapp_id);
106
107 /**
108 * @brief Check if a dApp is registered
109 *
110 * @param dapp_id dApp identifier
111 * @return true if registered
112 */
113 bool is_dapp_registered(uint32_t dapp_id) const;
114
115 /**
116 * @brief Get list of registered dApp IDs
117 */
118 std::vector<uint32_t> get_registered_dapps() const;
119
120 // =========================================================================
121 // Subscription Management (E3 Subscription flow)
122 // =========================================================================
123
124 /**
125 * @brief Add a subscription between dApp and RAN function
126 *
127 * @param dapp_id dApp identifier
128 * @param ran_function_id RAN function identifier (0-255)
129 * @param telemetry_ids Telemetry IDs the dApp wants
130 * @param control_ids Control IDs the dApp wants
131 * @param periodicity_us Reporting periodicity in microseconds (0 = SM default)
132 * @return std::pair containing:
133 * - ErrorCode::SUCCESS on success, or error code on failure
134 * - The assigned subscription ID (valid only if ErrorCode::SUCCESS)
135 * @return ErrorCode::DAPP_NOT_REGISTERED if dApp not registered
136 * @return ErrorCode::SUBSCRIPTION_EXISTS if already subscribed
137 */
138 std::pair<ErrorCode, uint32_t> add_subscription(
139 uint32_t dapp_id,
140 uint32_t ran_function_id,
141 const std::vector<uint32_t>& telemetry_ids = {},
142 const std::vector<uint32_t>& control_ids = {},
143 uint32_t periodicity_us = 0);
144
145 /**
146 * @brief Remove a subscription between dApp and RAN function
147 *
148 * @param dapp_id dApp identifier
149 * @param ran_function_id RAN function identifier
150 * @return ErrorCode::SUCCESS on success
151 * @return ErrorCode::SUBSCRIPTION_NOT_FOUND if subscription doesn't exist
152 */
153 ErrorCode remove_subscription(uint32_t dapp_id, uint32_t ran_function_id);
154
155 /**
156 * @brief Remove a subscription by its ID
157 *
158 * @param dapp_id dApp identifier
159 * @param subscription_id Subscription ID
160 * @return ErrorCode::SUCCESS on success
161 * @return ErrorCode::SUBSCRIPTION_NOT_FOUND if subscription doesn't exist
162 */
163 ErrorCode remove_subscription_by_id(uint32_t dapp_id, uint32_t subscription_id);
164
165 /**
166 * @brief Check if dApp is subscribed to a RAN function
167 */
168 bool is_subscribed(uint32_t dapp_id, uint32_t ran_function_id) const;
169
170 /**
171 * @brief Get all RAN functions a dApp is subscribed to
172 */
173 std::vector<uint32_t> get_dapp_subscriptions(uint32_t dapp_id) const;
174
175 /**
176 * @brief Get all dApps subscribed to a RAN function
177 */
178 std::vector<uint32_t> get_subscribed_dapps(uint32_t ran_function_id) const;
179
180 /**
181 * @brief Get subscription details for a specific dApp + RAN function pair
182 *
183 * @return nullptr if no such subscription exists
184 */
185 const SubscriptionDetails* get_subscription_details(uint32_t dapp_id, uint32_t ran_function_id) const;
186
187 /**
188 * @brief Get count of subscribers for a RAN function
189 */
190 size_t get_subscriber_count(uint32_t ran_function_id) const;
191
192 // =========================================================================
193 // RAN Function Management
194 // =========================================================================
195
196 /**
197 * @brief Get all RAN functions that have at least one subscriber
198 */
199 std::vector<uint32_t> get_active_ran_functions() const;
200
201 /**
202 * @brief Check if any dApp is subscribed to a RAN function
203 */
204 bool has_subscribers(uint32_t ran_function_id) const;
205
206 // =========================================================================
207 // Statistics
208 // =========================================================================
209
210 /**
211 * @brief Get total number of registered dApps
212 */
213 size_t dapp_count() const;
214
215 /**
216 * @brief Get total number of active subscriptions
217 */
218 size_t subscription_count() const;
219
220 /**
221 * @brief Clear all registrations and subscriptions
222 */
223 void clear();
224
225private:
226 // Using shared_mutex for read-heavy workloads
227 mutable std::shared_mutex mutex_;
228
229 // dApp ID -> registration entry
230 std::unordered_map<uint32_t, DAppEntry> registered_dapps_;
231
232 // dApp ID -> set of subscribed RAN function IDs
233 std::unordered_map<uint32_t, std::unordered_set<uint32_t>> dapp_subscriptions_;
234
235 // RAN function ID -> set of subscribed dApp IDs (reverse index for fast lookup)
236 std::unordered_map<uint32_t, std::unordered_set<uint32_t>> ran_function_subscribers_;
237
238 // Subscription ID -> (dApp ID, RAN function ID) mapping
239 std::unordered_map<uint32_t, std::pair<uint32_t, uint32_t>> subscription_ids_;
240
241 // (dApp ID, RAN function ID) -> Subscription ID reverse mapping
242 std::unordered_map<uint64_t, uint32_t> subscription_id_reverse_;
243
244 // (dApp ID, RAN function ID) -> per-subscription metadata
245 std::unordered_map<uint64_t, SubscriptionDetails> subscription_details_;
246
247 // Callback for SM lifecycle events
248 SmLifecycleCallback sm_lifecycle_callback_;
249
250 // Helper to create composite key for subscription
251 static uint64_t make_sub_key(uint32_t dapp_id, uint32_t ran_func_id) {
252 return (static_cast<uint64_t>(dapp_id) << 32) | ran_func_id;
253 }
254
255 /**
256 * @brief Check if SM should be started/stopped and invoke callback
257 *
258 * Called after subscription changes to notify the E3Agent about
259 * RAN functions that need their SMs started or stopped.
260 *
261 * @param ran_function_id RAN function that changed
262 * @param had_subscribers Whether it had subscribers before the change
263 */
264 void check_sm_lifecycle(uint32_t ran_function_id, bool had_subscribers);
265};
266
267} // namespace libe3
268
269#endif // LIBE3_SUBSCRIPTION_MANAGER_HPP
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.
SubscriptionManager(const SubscriptionManager &)=delete
SubscriptionManager & operator=(SubscriptionManager &&)=delete
std::pair< ErrorCode, uint32_t > register_dapp()
Register a dApp from E3 Setup Request.
SubscriptionManager & operator=(const SubscriptionManager &)=delete
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.
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.
SubscriptionManager(SubscriptionManager &&)=delete
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.
ErrorCode
Error codes returned by libe3 operations Use shared error-code list (see libe3/error_codes....
Definition types.hpp:141
std::function< void(uint32_t ran_function_id, bool should_start)> SmLifecycleCallback
Callback type for SM lifecycle events.
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
E3AP Protocol Types - Vendor-neutral type definitions.