3 ==================================================================================
4 Copyright (c) 2020 Nokia
5 Copyright (c) 2020 AT&T Intellectual Property.
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
11 http://www.apache.org/licenses/LICENSE-2.0
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18 ==================================================================================
23 The C++ framework allows the programmer to create an xApp object instance, and to use that instance as
25 The xApp object provides a message level interface to the RIC Message Router (RMR), including the ability
26 to register callback functions which the instance will drive as messages are received; much in the same
27 way that an X-windows application is driven by the window manager for all activity.
28 The xApp may also choose to use its own send/receive loop, and thus is not required to use the callback
29 driver mechanism provided by the framework.
31 &h1(The Framework API)
32 The C++ framework API consists of the creation of the xApp object, and invoking desired functions via
33 the instance of the object.
34 The following paragraphs cover the various steps involved to create an xApp instance, wait for a route
35 table to arrive, send a message, and wait for messages to arrive.
38 Starting with version 2.0.0 the framwork introduces a &ital(namespace) of &cw(xapp) for the following
51 This is a breaking change and as such the major version was bumpped from 1 to 2.
53 &h2(Creating the xApp instance)
54 The creation of the xApp instance is as simple as invoking the object's constructor with two required
59 &beg_dlist(.5i:&ditemtext)
60 &di(port) A C string (pointer to char) which defines the port that RMR will open to listen for connections.
63 &di(wait) A Boolean value which indicates whether or not the initialization process should wait for
64 the arrival of a valid route table before completing.
65 When true is supplied, the initialization will not complete until RMR has received a valid route table
66 (or one is located via the &cw(RMR_SEED_RT) environment variable).
71 The following code sample illustrates the simplicity of creating the instance of the xApp object.
76 #include <ricxfcpp/xapp.hpp>
78 std::unique_ptr<Xapp> xapp;
79 char* listen_port = (char *) "4560"; //RMR listen port
80 bool wait4table = true; // wait for a route table
82 xapp = std::unique_ptr<Xapp>(
83 new Xapp( listen_port, wait4table ) );
86 &fig_cen( Creating an xAPP instance.)
89 From a compilation perspective, the following is the simple compiler invocation string needed to compile
90 and link the above program (assuming that the sample code exists in a file called &cw(man_ex1.cpp).
95 g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread
99 The above program, while complete and capable of being compiled, does nothing useful.
100 When invoked, RMR will be initialized and will begin listening for a route table; blocking the return
101 to the main program until one is received.
102 When a valid route table arrives, initialization will complete and the program will exit as there is
103 no code following the instruction to create the object.
105 &h1(Listening For Messages)
106 The program in the previous example can be extended with just a few lines of code to enable it to receive
107 and process messages.
108 The application needs to register a callback function for each message type which it desires to process.
111 Once registered, each time a message is received the registered callback for the message type will be
112 invoked by the framework.
114 &h2(Callback Signature)
115 As with most callback related systems, a callback must have a well known function signature which generally
116 passes event related information and a "user" data pointer which was registered with the function.
117 The following is the prototype which callback functions must be defined with:
121 void cb_name( xapp::Message& m, int mtype, int subid,
122 int payload_len, xapp::Msg_component payload,
125 &fig_cen( Callback function signature)
128 The parameters passed to the callback function are as follows:
132 &beg_dlist(1i:&ditemtext)
133 &di(m) A reference to the Message that was received.
136 &di(mtype) The message type (allows for disambiguation if the callback is registered for multiple message types).
139 &di(subid) The subscription ID from the message.
142 &di(payload len) The number of bytes which the sender has placed into the payload.
145 &di(payload) A direct reference (smart pointer) to the payload. (The smart pointer is wrapped in a
146 special class in order to provide a custom destruction function without burdening the xApp developer
147 with that knowledge.)
149 &di(user data) A pointer to user data. This is the pointer that was provided when the function was registered.
154 To illustrate the use of a callback function, the previous code example has been extended to add the
155 function, register it for message types 1000 and 1001, and to invoke the &cw(Run()) function in the
156 framework (explained in the next section).
160 #include <ricxfcpp/xapp.hpp>
161 long m1000_count = 0; // message counters, one for each type
162 long m1001_count = 0;
164 // callback function that will increase the appropriate counter
165 void cbf( xapp::Message& mbuf, int mtype, int subid, int len,
166 xapp::Msg_component payload, void* data ) {
169 if( (counter = (long *) data) != NULL ) {
175 std::unique_ptr<Xapp> xapp;
176 char* listen_port = (char *) "4560";
177 bool wait4table = false;
179 xapp = std::unique_ptr<Xapp>(
180 new Xapp( listen_port, wait4table ) );
182 // register the same callback function for both msg types
183 xapp->Add_msg_cb( 1000, cbf, (void *) &m1000_count );
184 xapp->Add_msg_cb( 1001, cbf, (void *) &m1001_count );
186 xapp->Run( 1 ); // start the callback driver
189 &fig_cen( Callback function example.)
192 As before, the program does nothing useful, but now it will execute and receive messages.
194 the same function can be used to increment the appropriate counter simply by providing a pointer to the
195 counter as the user data when the callback function is registered.
196 In addition, a subtle change from the previous example has been to set the wait for table flag to &cw(false.)
199 For an xApp that is a receive only application (never sends) it is not necessary to wait for RMR
200 to receive a table from the Route Manager.
202 &h2(Registering A Default Callback)
203 The xApp may also register a default callback function such that the function will be invoked for any
204 message that does not have a registered callback.
205 If the xAPP does not register a default callback, any message which cannot be mapped to a known callback
206 function is silently dropped.
207 A default callback is registered by providing a &ital(generic) message type of &cw(xapp->DEFAULT_CALLBACK)
208 on an &cw(Add_msg_cb) call.
210 &h2(The Framework Callback Driver)
211 The &cw(Run()) function within the Xapp object is invoked to start the callback driver, and the xApp
212 should not expect the function
213 to return under most circumstances.
214 The only parameter that the &cw(Run()) function expects is the number of threads to start.
215 For each thread requested, the framework will start a listener thread which will allow received messages
216 to be processed in parallel.
217 If supplying a value greater than one, the xApp must ensure that the callback functions are thread safe
218 as it is very likely that
219 the same callback function will be invoked concurrently from multiple threads.
221 &h1(Sending Messages)
222 It is very likely that most xApps will need to send messages and will not operate in "receive only" mode.
223 Sending the message is a function of the message object itself and can take one of two forms:
228 &li Replying to the sender of a received message
231 &li Sending a message (routed based on the message type and subscription ID)
236 When replying to the sender, the message type and subscription ID are not used to determine the destination
237 of the message; RMR ensures that the message is sent back to the originating xApp. The xApp may still need
238 to change the message type and/or the subscription ID in the message prior to using the reply function.
241 To provide for both situations, two reply functions are supported by the Message object as illustrated
242 with the following prototypes.
246 bool Send_response( int mtype, int subid, int response_len,
247 std:shared_ptr<unsigned char> response );
249 bool Send_response( int response_len, std::shared_ptr<unsigned char> response );
251 &fig_cen( Reply function prototypes. )
254 In the first prototype the xApp must supply the new message type and subscription ID values, where the
255 second function uses the values which are currently set in the message.
256 Further, the new payload contents, and length, are supplied to both functions; the framework ensures
257 that the message is large enough to accommodate the payload, reallocating it if necessary, and copies
258 the response into the message payload prior to sending.
259 Should the xApp need to change either the message type, or the subscription ID, but not both, the &cw(NO_CHANGE)
260 constant can be used as illustrated below.
264 msg->Send_response( xapp::Message::NO_CHANGE, xapp::Message::NO_SUBID,
265 pl_length, (unsigned char *) payload );
267 &fig_cen( Send response prototype. )
270 In addition to the two function prototypes for &cw(Send_response()) there are two additional prototypes
271 which allow the new payload to be supplied as a shared smart pointer.
272 The other parameters to these functions are identical to those illustrated above, and thus are not presented
276 The &cw(Send_msg()) set of functions supported by the Message object are identical to the &cw(Send_response())
277 functions and are shown below.
281 bool Send_msg( int mtype, int subid, int payload_len,
282 std::shared_ptr<unsigned char> payload );
284 bool Send_msg( int mtype, int subid, int payload_len,
285 unsigned char* payload );
287 bool Send_msg( int payload_len,
288 std::shared_ptr<unsigned char> payload );
290 bool Send_msg( int payload_len, unsigned char* payload );
292 &fig_cen( Send function prototypes. )
295 Each send function accepts the message, copies in the payload provided, sets the message type and subscription
296 ID (if provided), and then causes the message to be sent.
297 The only difference between the &cw(Send_msg()) and &cw(Send_response()) functions is that the destination
298 of the message is selected based on the mapping of the message type and subscription ID using the current
299 routing table known to RMR.
301 &h2(Direct Payload Manipulation)
302 For some applications, it might be more efficient to manipulate the payload portion of an Xapp Message
303 in place, rather than creating it and relying on a buffer copy when the message is finally sent.
304 To achieve this, the xApp must either use the smart pointer to the payload passed to the callback function,
305 or retrieve one from the message using &cw(Get_payload()) when working with a message outside of a
307 Once the smart pointer is obtained, the pointer's get() function can be used to directly reference the
308 payload (unsigned char) bytes.
311 When working directly with the payload, the xApp must take care not to write more than the actual payload
312 size which can be extracted from the Message object using the &cw(Get_available_size()) function.
315 When sending a message where the payload has been directly altered, and no extra buffer copy is needed,
316 a NULL pointer should be passed to the Message send function.
317 The following illustrates how the payload can be directly manipulated and returned to the sender (for
318 simplicity, there is no error handling if the payload size of the received message isn't large enough
319 for the response string, the response is just not sent).
323 Msg_component payload; // smart reference
324 int pl_size; // max size of payload
326 payload = msg->Get_payload();
327 pl_size = msg->Get_available_size();
328 if( snprintf( (char *) payload.get(), pl_size,
329 "Msg Received\n" ) < pl_size ) {
330 msg->Send_response( M_TYPE, SID, strlen( raw_pl ), NULL );
333 &fig_cen( Send message without buffer copy. )
336 &h2(Sending Multiple Responses)
337 It is likely that the xApp will wish to send multiple responses back to the process that sent a message
338 that triggered the callback.
339 The callback function may invoke the &cw(Send_response()) function multiple times before returning.
342 After each call, the Message retains the necessary information to allow for a subsequent invocation to
344 It should be noted though, that after the first call to &cw({Send_response()) the original payload
345 will be lost; if necessary, the xApp must make a copy of the payload before the first response call is
348 &h2(Message Allocation)
349 Not all xApps will be "responders," meaning that some xApps will need to send one or more messages before
350 they can expect to receive any messages back.
351 To accomplish this, the xApp must first allocate a message buffer, optionally initialising the payload,
352 and then using the message's &cw(Send_msg()) function to send a message out.
353 The framework's &cw(Alloc_msg()) function can be used to create a Message object with a desired payload
356 &h1(Framework Provided Callbacks)
357 The framework itself may provide message handling via the driver such that the xApp might not need to
358 implement some message processing functionality.
359 Initially, the C++ framework will provide a default callback function to handle the RMR based health
361 This callback function will assume that if the message was received, and the callback invoked, that all
362 is well and will reply with an OK state.
363 If the xApp should need to override this simplistic response, all it needs to do is to register its
364 own callback function for the health check message type.