Add API allowing xAPPs to send alarm messages
[ric-plt/xapp-frame-cpp.git] / doc / src / user / cpp_frame.im
1
2 .if false
3 ==================================================================================
4     Copyright (c) 2020 Nokia
5     Copyright (c) 2020 AT&T Intellectual Property.
6
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
10
11        http://www.apache.org/licenses/LICENSE-2.0
12
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 ==================================================================================
19
20 .fi
21
22 &h1(Introduction)
23 The C++ framework allows the programmer to create an xApp object instance, and to use that instance as
24 the logic base.
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.
30
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.
36
37 &h2(The Namespace)
38 Starting with version 2.0.0 the framwork introduces a &ital(namespace) of &cw(xapp) for the following
39 classes and types:
40 &half_space
41 &indent
42 &beg_list(&lic1)
43         &li Alarm
44         &li Jhash
45         &li Message
46         &li Msg_component
47 &end_list
48 &uindent
49 &space
50
51 This is a breaking change and as such the major version was bumpped from 1 to 2.
52
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
55 parameters:
56
57 &half_space
58 &indent
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.
61     &half_space
62
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).
67 &end_dlist
68 &uindent
69 &space
70
71 The following code sample illustrates the simplicity of creating the instance of the xApp object.
72 &half_space
73
74 &ex_start
75     #include <memory>
76     #include <ricxfcpp/xapp.hpp>
77     int main( ) {
78         std::unique_ptr<Xapp> xapp;
79         char* listen_port = (char *) "4560";    //RMR listen port
80         bool  wait4table = true;            // wait for a route table
81
82         xapp = std::unique_ptr<Xapp>(
83               new Xapp( listen_port, wait4table ) );
84     }
85 &ex_end
86 &fig_cen( Creating an xAPP instance.)
87 &space
88
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).
91 &half_space
92
93
94 &ex_start
95    g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread
96 &ex_end
97 &space
98
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.
104
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.
109 &space
110
111 Once registered, each time a message is received the registered callback for the message type will be
112 invoked by the framework.
113
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:
118 &half_space
119
120 &ex_start
121     void cb_name( xapp::Message& m, int mtype, int subid,
122           int payload_len, xapp::Msg_component payload,
123           void* usr_data );
124 &ex_end
125 &fig_cen( Callback function signature)
126 &space
127
128 The parameters passed to the callback function are as follows:
129 &mult_space( .1 )
130
131 &indent
132 &beg_dlist(1i:&ditemtext)
133     &di(m) A reference to the Message that was received.
134     &half_space
135
136     &di(mtype) The message type (allows for disambiguation if the callback is registered for multiple message types).
137     &half_space
138
139     &di(subid) The subscription ID from the message.
140     &half_space
141
142     &di(payload len) The number of bytes which the sender has placed into the payload.
143     &half_space
144
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.)
148     &half_space
149     &di(user data) A pointer to user data. This is the pointer that was provided when the function was registered.
150 &end_dlist
151 &uindent
152 &space
153
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).
157
158 &ex_start
159     #include <memory>
160     #include <ricxfcpp/xapp.hpp>
161     long m1000_count = 0;    // message counters, one for each type
162     long m1001_count = 0;
163
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 ) {
167         long* counter;
168
169         if( (counter = (long *) data) != NULL ) {
170             (*counter)++;
171         }
172     }
173
174     int main( ) {
175         std::unique_ptr<Xapp> xapp;
176         char* listen_port = (char *) "4560";
177         bool  wait4table = false;
178
179         xapp = std::unique_ptr<Xapp>(
180               new Xapp( listen_port, wait4table ) );
181
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 );
185
186         xapp->Run( 1 );        // start the callback driver
187     }
188 &ex_end
189 &fig_cen( Callback function example.)
190 &space
191
192 As before, the program does nothing useful, but now it will execute and receive messages.
193 For this example,
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.)
197 &space
198
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.
201
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.
209
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.
220
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:
224
225 &half_space
226 &indent
227 &beg_list(&lic1)
228     &li Replying to the sender of a received message
229     &half_space
230
231     &li Sending a message (routed based on the message type and subscription ID)
232 &end_list
233 &uindent
234 &space
235
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.
239 &space
240
241 To provide for both situations, two reply functions are supported by the Message object as illustrated
242 with the following prototypes.
243 &half_space
244
245 &ex_start
246    bool Send_response(  int mtype, int subid, int response_len,
247         std:shared_ptr<unsigned char> response );
248
249    bool Send_response(  int response_len, std::shared_ptr<unsigned char> response );
250 &ex_end
251 &fig_cen( Reply function prototypes. )
252 &space
253
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.
261 &half_space
262
263 &ex_start
264     msg->Send_response( xapp::Message::NO_CHANGE, xapp::Message::NO_SUBID,
265         pl_length, (unsigned char *) payload );
266 &ex_end
267 &fig_cen( Send response prototype. )
268 &space
269
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
273 here.
274 &space
275
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.
278 &half_space
279
280 &ex_start
281     bool Send_msg( int mtype, int subid, int payload_len,
282         std::shared_ptr<unsigned char> payload );
283
284     bool Send_msg( int mtype, int subid, int payload_len,
285         unsigned char* payload );
286
287     bool Send_msg( int payload_len,
288         std::shared_ptr<unsigned char> payload );
289
290     bool Send_msg( int payload_len, unsigned char* payload );
291 &ex_end
292 &fig_cen( Send function prototypes. )
293 &space
294
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.
300
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
306 callback function.
307 Once the smart pointer is obtained, the pointer's get() function can be used to directly reference the
308 payload (unsigned char) bytes.
309 &space
310
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.
313 &space
314
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).
320 &half_space
321
322 &ex_start
323     Msg_component payload;  // smart reference
324     int pl_size;            // max size of payload
325
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 );
331     }
332 &ex_end
333 &fig_cen( Send message without buffer copy. )
334 &space
335
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.
340 &space
341
342 After each call, the Message retains the necessary information to allow for a subsequent invocation to
343 send more data.
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
346 made.
347
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
354 size.
355
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
360 check messages.
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.