Skip to content

Commit f4ba064

Browse files
Create new callback mechanism for dApp status to the E2 Agent
* Implemented callback that is triggered each time the status of a dApp changes * Added documentation of new functionalities and missing ones, and added new tests for new functionalities, bumped version to 0.0.2
1 parent 92855e9 commit f4ba064

11 files changed

Lines changed: 373 additions & 1 deletion

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,11 @@ The main facade class for RAN vendors.
228228
| `is_running()` | Check if agent is running |
229229
| `register_sm(sm)` | Register a Service Model |
230230
| `get_available_ran_functions()` | Get available RAN function IDs from registered SMs |
231+
| `set_dapp_report_handler(handler)` | Set callback for incoming dApp reports (dApp → RAN) |
232+
| `set_dapp_status_changed_handler(handler)` | Set callback for dApp status changes (connect, disconnect, subscribe, unsubscribe) |
231233
| `send_indication(dapp_id, ran_function_id, data)` | Send an indication to a specific dApp |
234+
| `send_xapp_control(dapp_id, ran_function_id, data)` | Forward an xApp control action to a specific dApp |
235+
| `send_message_ack(request_id, response_code)` | Send a message acknowledgment (positive or negative) to a dApp |
232236
| `get_registered_dapps()` | Get list of registered dApp IDs |
233237
| `get_dapp_subscriptions(dapp_id)` | Get subscriptions for a dApp |
234238
| `get_ran_function_subscribers(ran_function_id)` | Get subscribers for a RAN function |

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.0.1
1+
0.0.2

examples/simple_agent.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,13 @@ int main(int argc, char* argv[]) {
259259
return 1;
260260
}
261261

262+
// Handle dApp status changes (connect, subscribe, unsubscribe, disconnect)
263+
agent.set_dapp_status_changed_handler([&agent]() {
264+
std::cout << "[STATUS] dApp status changed — "
265+
<< "dApps: " << agent.dapp_count()
266+
<< ", subscriptions: " << agent.subscription_count() << "\n";
267+
});
268+
262269
// Handle dApp reports for the RAN
263270
agent.set_dapp_report_handler([](const libe3::DAppReport& report) {
264271
// In the RAN report is sent to the xApp through E2, here we just decode it as en example

include/libe3/c_api.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,24 @@ typedef void (*e3_dapp_report_cb)(
4444
size_t report_data_len
4545
);
4646

47+
/** Callback when dApp status changes (connect, disconnect, subscribe, unsubscribe). */
48+
typedef void (*e3_dapp_status_changed_cb)(void);
49+
50+
/**
51+
* @brief Set callback for dApp status changes.
52+
*
53+
* Called when a dApp connects, disconnects, subscribes, or unsubscribes.
54+
* Pass NULL as \p handler to clear the callback.
55+
*
56+
* @param agent Agent handle
57+
* @param handler Callback invoked on each dApp status change
58+
* @return \ref e3_error_t (see \ref libe3::ErrorCode; 0 == SUCCESS)
59+
*/
60+
e3_error_t e3_agent_set_dapp_status_changed_handler(
61+
e3_agent_handle_t* agent,
62+
e3_dapp_status_changed_cb handler
63+
);
64+
4765
/**
4866
* @brief Descriptor describing a ServiceModel implemented in C.
4967
*

include/libe3/e3_agent.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ class E3Interface;
2626
/** Callback for incoming dApp reports (dApp → RAN). Set before start() to handle reports. */
2727
using DAppReportHandler = std::function<void(const DAppReport&)>;
2828

29+
/** Callback when dApp status changes (connect, disconnect, subscribe, unsubscribe). */
30+
using DAppStatusChangedHandler = std::function<void()>;
31+
2932
/**
3033
* @brief E3Agent - Main façade for RAN vendor integration
3134
*
@@ -162,6 +165,13 @@ class E3Agent {
162165
*/
163166
void set_dapp_report_handler(DAppReportHandler handler);
164167

168+
/**
169+
* @brief Set callback for dApp status changes.
170+
* Called when a dApp connects, disconnects, subscribes, or unsubscribes.
171+
* Call before start().
172+
*/
173+
void set_dapp_status_changed_handler(DAppStatusChangedHandler handler);
174+
165175
// =========================================================================
166176
// Manual Operations
167177
// =========================================================================

include/libe3/e3_interface.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class E3Agent;
3434
using SetupRequestHandler = std::function<ResponseCode(const SetupRequest&, SetupResponse&)>;
3535
using SubscriptionRequestHandler = std::function<ResponseCode(const SubscriptionRequest&)>;
3636
using DAppReportHandler = std::function<void(const DAppReport&)>;
37+
using DAppStatusChangedHandler = std::function<void()>;
3738

3839
/**
3940
* @brief E3Interface - Internal protocol coordination
@@ -137,6 +138,12 @@ class E3Interface {
137138
dapp_report_handler_ = std::move(handler);
138139
}
139140

141+
void set_dapp_status_changed_handler(DAppStatusChangedHandler handler) {
142+
dapp_status_changed_handler_ = std::move(handler);
143+
}
144+
145+
void notify_dapp_status_changed();
146+
140147
private:
141148
// Configuration
142149
E3Config config_;
@@ -160,6 +167,8 @@ class E3Interface {
160167
// Event handlers
161168
DAppReportHandler dapp_report_handler_;
162169

170+
DAppStatusChangedHandler dapp_status_changed_handler_;
171+
163172
// =========================================================================
164173
// Thread Entry Points
165174
// =========================================================================

src/c_api.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,4 +358,23 @@ const char* e3_error_to_string(e3_error_t code) {
358358
default: return "UNKNOWN_ERROR_CODE";
359359
}
360360
}
361+
362+
e3_error_t e3_agent_set_dapp_status_changed_handler(
363+
e3_agent_handle_t* agent,
364+
e3_dapp_status_changed_cb handler
365+
) {
366+
if (!agent || !agent->agent) return static_cast<int>(ErrorCode::INVALID_PARAM);
367+
368+
if (!handler) {
369+
agent->agent->set_dapp_status_changed_handler(DAppStatusChangedHandler{});
370+
return static_cast<int>(ErrorCode::SUCCESS);
371+
}
372+
373+
agent->agent->set_dapp_status_changed_handler(
374+
[handler]() { handler(); }
375+
);
376+
377+
return static_cast<int>(ErrorCode::SUCCESS);
378+
}
379+
361380
} // extern "C"

src/core/e3_agent.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ struct E3Agent::Impl {
2323
E3Config config;
2424
std::unique_ptr<E3Interface> interface;
2525
DAppReportHandler dapp_report_handler;
26+
DAppStatusChangedHandler dapp_status_changed_handler;
2627

2728
explicit Impl(E3Config cfg) : config(std::move(cfg)) {}
2829
};
@@ -66,6 +67,10 @@ ErrorCode E3Agent::init() {
6667
if (impl_->dapp_report_handler) {
6768
impl_->interface->set_dapp_report_handler(impl_->dapp_report_handler);
6869
}
70+
71+
if (impl_->dapp_status_changed_handler) {
72+
impl_->interface->set_dapp_status_changed_handler(impl_->dapp_status_changed_handler);
73+
}
6974

7075
E3_LOG_INFO(LOG_TAG) << "E3Agent initialized successfully";
7176
return ErrorCode::SUCCESS;
@@ -148,6 +153,13 @@ void E3Agent::set_dapp_report_handler(DAppReportHandler handler) {
148153
}
149154
}
150155

156+
void E3Agent::set_dapp_status_changed_handler(DAppStatusChangedHandler handler) {
157+
impl_->dapp_status_changed_handler = std::move(handler);
158+
if (impl_->interface && impl_->dapp_status_changed_handler) {
159+
impl_->interface->set_dapp_status_changed_handler(impl_->dapp_status_changed_handler);
160+
}
161+
}
162+
151163
// =========================================================================
152164
// Manual Operations
153165
// =========================================================================

src/core/e3_interface.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,12 @@ ErrorCode E3Interface::register_sm(std::unique_ptr<ServiceModel> sm) {
200200
return SmRegistry::instance().register_sm(std::move(sm));
201201
}
202202

203+
void E3Interface::notify_dapp_status_changed() {
204+
if (dapp_status_changed_handler_) {
205+
dapp_status_changed_handler_();
206+
}
207+
}
208+
203209
// =========================================================================
204210
// Thread Entry Points
205211
// =========================================================================
@@ -433,6 +439,7 @@ void E3Interface::handle_setup_request(const SetupRequest& request, uint32_t req
433439
if (result == ErrorCode::SUCCESS) {
434440
response_code = ResponseCode::POSITIVE;
435441
E3_LOG_INFO(LOG_TAG) << "dApp '" << request.dapp_name << "' registered with assigned ID " << assigned_dapp_id;
442+
notify_dapp_status_changed();
436443
} else {
437444
E3_LOG_ERROR(LOG_TAG) << "Failed to register dApp '" << request.dapp_name << "': " << error_code_to_string(result);
438445
}
@@ -508,6 +515,9 @@ void E3Interface::handle_subscription_request(const SubscriptionRequest& request
508515
E3_LOG_INFO(LOG_TAG) << "Subscription added: dApp " << request.dapp_identifier
509516
<< " -> RAN function " << request.ran_function_identifier
510517
<< " (subscription_id=" << subscription_id << ")";
518+
if (result == ErrorCode::SUCCESS) {
519+
notify_dapp_status_changed();
520+
}
511521
} else {
512522
E3_LOG_ERROR(LOG_TAG) << "Failed to add subscription: "
513523
<< error_code_to_string(result);
@@ -543,6 +553,7 @@ void E3Interface::handle_subscription_delete(const SubscriptionDelete& del, uint
543553
if (result == ErrorCode::SUCCESS) {
544554
response_code = ResponseCode::POSITIVE;
545555
E3_LOG_INFO(LOG_TAG) << "Subscription " << del.subscription_id << " removed for dApp " << del.dapp_identifier;
556+
notify_dapp_status_changed();
546557
} else {
547558
E3_LOG_ERROR(LOG_TAG) << "Failed to remove subscription: "
548559
<< error_code_to_string(result);
@@ -610,6 +621,7 @@ void E3Interface::handle_dapp_disconnection(uint32_t dapp_id) {
610621
if (result == ErrorCode::SUCCESS) {
611622
E3_LOG_INFO(LOG_TAG) << "dApp " << dapp_id << " unregistered, "
612623
<< subscriptions.size() << " subscriptions removed";
624+
notify_dapp_status_changed();
613625
} else {
614626
E3_LOG_ERROR(LOG_TAG) << "Failed to unregister dApp " << dapp_id << ": "
615627
<< error_code_to_string(result);

tests/test_e3_agent.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,71 @@ TEST(E3Agent_move_semantics) {
202202
ASSERT_EQ(funcs.size(), 1u);
203203
}
204204

205+
TEST(E3Agent_set_dapp_status_changed_handler_before_init) {
206+
E3Config config;
207+
config.ran_identifier = "status-cb-test";
208+
209+
E3Agent agent(std::move(config));
210+
211+
bool called = false;
212+
agent.set_dapp_status_changed_handler([&called]() {
213+
called = true;
214+
});
215+
216+
// Handler is stored but cannot fire until the agent is running and a
217+
// dApp actually connects/subscribes. Verify that setting it before init
218+
// does not crash and the agent initialises normally.
219+
auto result = agent.init();
220+
ASSERT_EQ(error_to_int(result), error_to_int(ErrorCode::SUCCESS));
221+
}
222+
223+
TEST(E3Agent_set_dapp_status_changed_handler_after_init) {
224+
E3Config config;
225+
config.ran_identifier = "status-cb-after-init";
226+
227+
E3Agent agent(std::move(config));
228+
agent.init();
229+
230+
bool called = false;
231+
agent.set_dapp_status_changed_handler([&called]() {
232+
called = true;
233+
});
234+
235+
// Handler is forwarded to the already-created E3Interface.
236+
// No crash, and agent state remains valid.
237+
ASSERT_NE(state_to_int(agent.state()), state_to_int(AgentState::UNINITIALIZED));
238+
}
239+
240+
TEST(E3Agent_set_dapp_status_changed_handler_nullptr_clears) {
241+
E3Config config;
242+
config.ran_identifier = "status-cb-clear";
243+
244+
E3Agent agent(std::move(config));
245+
246+
// Set a handler then clear it with an empty function.
247+
agent.set_dapp_status_changed_handler([]() {});
248+
agent.set_dapp_status_changed_handler(DAppStatusChangedHandler{});
249+
250+
auto result = agent.init();
251+
ASSERT_EQ(error_to_int(result), error_to_int(ErrorCode::SUCCESS));
252+
}
253+
254+
TEST(E3Agent_set_dapp_status_changed_handler_replace) {
255+
E3Config config;
256+
config.ran_identifier = "status-cb-replace";
257+
258+
E3Agent agent(std::move(config));
259+
260+
int counter = 0;
261+
agent.set_dapp_status_changed_handler([&counter]() { counter = 1; });
262+
agent.set_dapp_status_changed_handler([&counter]() { counter = 2; });
263+
264+
// The second handler should replace the first. Verify no crash and
265+
// the agent still initialises correctly.
266+
auto result = agent.init();
267+
ASSERT_EQ(error_to_int(result), error_to_int(ErrorCode::SUCCESS));
268+
}
269+
205270
TEST(E3Agent_destructor_stops) {
206271
bool was_running = false;
207272
{

0 commit comments

Comments
 (0)