Add support for config file parsing and watching
[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 instance of the &cw(Xapp) object which then
24 can be used as a foundation for the application.
25 The &cw(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 &h2( Termonology )
32 To avoid confusion the term &bold( xAPP ) is used in this document to refer to the user's application
33 code which is creating &cw( Xapp, ) and related objects provided by the &ital( framework. )
34 The use of &ital( framework ) should be taken to mean any of the classes and/or support functions
35 which are provided by the &cw( ricxfcpp ) library.
36
37 &h1( The Framework API )
38 The C++ framework API consists of the creation of the xApp object, and invoking desired functions via
39 the instance of the object.
40 The following paragraphs cover the various steps involved to create an xApp instance, wait for a route
41 table to arrive, send a message, and wait for messages to arrive.
42
43 &h2(The Namespace)
44 Starting with version 2.0.0 the framwork introduces a &ital(namespace) of &cw(xapp) for the following
45 classes and types:
46 &half_space
47 &indent
48 &beg_list(&lic1)
49         &li Alarm
50         &li Jhash
51         &li Message
52         &li Msg_component
53 &end_list
54 &uindent
55 &space
56
57 This is a breaking change and as such the major version was bumpped from 1 to 2.
58
59 &h2(Creating the xApp instance)
60 The creation of the xApp instance is as simple as invoking the object's constructor with two required
61 parameters:
62
63 &half_space
64 &indent
65 &beg_dlist(.5i:&ditemtext)
66     &di(port) A C string (pointer to char) which defines the port that RMR will open to listen for connections.
67     &half_space
68
69     &di(wait) A Boolean value which indicates whether or not the initialization process should wait for
70         the arrival of a valid route table before completing.
71         When true is supplied, the initialization will not complete until RMR has received a valid route table
72         (or one is located via the &cw(RMR_SEED_RT) environment variable).
73 &end_dlist
74 &uindent
75 &space
76
77 The following code sample illustrates the simplicity of creating the instance of the xApp object.
78 &half_space
79
80 &ex_start
81     #include <memory>
82     #include <ricxfcpp/xapp.hpp>
83     int main( ) {
84         std::unique_ptr<Xapp> xapp;
85         char* listen_port = (char *) "4560";    //RMR listen port
86         bool  wait4table = true;            // wait for a route table
87
88         xapp = std::unique_ptr<Xapp>(
89               new Xapp( listen_port, wait4table ) );
90     }
91 &ex_end
92 &fig_cen( Creating an xAPP instance.)
93 &space
94
95 From a compilation perspective, the following is the simple compiler invocation string needed to compile
96 and link the above program (assuming that the sample code exists in a file called &cw(man_ex1.cpp).
97 &half_space
98
99
100 &ex_start
101    g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread
102 &ex_end
103 &space
104
105 The above program, while complete and capable of being compiled, does nothing useful.
106 When invoked, RMR will be initialized and will begin listening for a route table; blocking the return
107 to the main program until one is received.
108 When a valid route table arrives, initialization will complete and the program will exit as there is
109 no code following the instruction to create the object.
110
111 &h1(Listening For Messages)
112 The program in the previous example can be extended with just a few lines of code to enable it to receive
113 and process messages.
114 The application needs to register a callback function for each message type which it desires to process.
115 &space
116
117 Once registered, each time a message is received the registered callback for the message type will be
118 invoked by the framework.
119
120 &h2(Callback Signature)
121 As with most callback related systems, a callback must have a well known function signature which generally
122 passes event related information and a "user" data pointer which was registered with the function.
123 The following is the prototype which callback functions must be defined with:
124 &half_space
125
126 &ex_start
127     void cb_name( xapp::Message& m, int mtype, int subid,
128           int payload_len, xapp::Msg_component payload,
129           void* usr_data );
130 &ex_end
131 &fig_cen( Callback function signature)
132 &space
133
134 The parameters passed to the callback function are as follows:
135 &mult_space( .1 )
136
137 &indent
138 &beg_dlist(1i:&ditemtext)
139     &di(m) A reference to the Message that was received.
140     &half_space
141
142     &di(mtype) The message type (allows for disambiguation if the callback is registered for multiple message types).
143     &half_space
144
145     &di(subid) The subscription ID from the message.
146     &half_space
147
148     &di(payload len) The number of bytes which the sender has placed into the payload.
149     &half_space
150
151     &di(payload) A direct reference (smart pointer) to the payload. (The smart pointer is wrapped in a
152             special class in order to provide a custom destruction function without burdening the xApp developer
153             with that knowledge.)
154     &half_space
155     &di(user data) A pointer to user data. This is the pointer that was provided when the function was registered.
156 &end_dlist
157 &uindent
158 &space
159
160 To illustrate the use of a callback function, the previous code example has been extended to add the
161 function, register it for message types 1000 and 1001, and to invoke the &cw(Run()) function in the
162 framework (explained in the next section).
163
164 &ex_start
165     #include <memory>
166     #include <ricxfcpp/xapp.hpp>
167     long m1000_count = 0;    // message counters, one for each type
168     long m1001_count = 0;
169
170     // callback function that will increase the appropriate counter
171     void cbf( xapp::Message& mbuf, int mtype, int subid, int len,
172                 xapp::Msg_component payload,  void* data ) {
173         long* counter;
174
175         if( (counter = (long *) data) != NULL ) {
176             (*counter)++;
177         }
178     }
179
180     int main( ) {
181         std::unique_ptr<Xapp> xapp;
182         char* listen_port = (char *) "4560";
183         bool  wait4table = false;
184
185         xapp = std::unique_ptr<Xapp>(
186               new Xapp( listen_port, wait4table ) );
187
188         // register the same callback function for both msg types
189         xapp->Add_msg_cb( 1000, cbf, (void *) &m1000_count );
190         xapp->Add_msg_cb( 1001, cbf, (void *) &m1001_count );
191
192         xapp->Run( 1 );        // start the callback driver
193     }
194 &ex_end
195 &fig_cen( Callback function example.)
196 &space
197
198 As before, the program does nothing useful, but now it will execute and receive messages.
199 For this example,
200 the same function can be used to increment the appropriate counter simply by providing a pointer to the
201 counter as the user data when the callback function is registered.
202 In addition, a subtle change from the previous example has been to set the wait for table flag to &cw(false.)
203 &space
204
205 For an xApp that is a receive only application (never sends) it is not necessary to wait for RMR
206 to receive a table from the Route Manager.
207
208 &h2(Registering A Default Callback)
209 The xApp may also register a default callback function such that the function will be invoked for any
210 message that does not have a registered callback.
211 If the xAPP does not register a default callback, any message which cannot be mapped to a known callback
212 function is silently dropped.
213 A default callback is registered by providing a &ital(generic) message type of &cw(xapp->DEFAULT_CALLBACK)
214 on an &cw(Add_msg_cb) call.
215
216 &h2(The Framework Callback Driver)
217 The &cw(Run()) function within the Xapp object is invoked to start the callback driver, and the xApp
218 should not expect the function
219 to return under most circumstances.
220 The only parameter that the &cw(Run()) function expects is the number of threads to start.
221 For each thread requested, the framework will start a listener thread which will allow received messages
222 to be processed in parallel.
223 If supplying a value greater than one, the xApp must ensure that the callback functions are thread safe
224 as it is very likely that
225 the same callback function will be invoked concurrently from multiple threads.
226
227 &h1(Sending Messages)
228 It is very likely that most xApps will need to send messages and will not operate in "receive only" mode.
229 Sending the message is a function of the message object itself and can take one of two forms:
230
231 &half_space
232 &indent
233 &beg_list(&lic1)
234     &li Replying to the sender of a received message
235     &half_space
236
237     &li Sending a message (routed based on the message type and subscription ID)
238 &end_list
239 &uindent
240 &space
241
242 When replying to the sender, the message type and subscription ID are not used to determine the destination
243 of the message; RMR ensures that the message is sent back to the originating xApp. The xApp may still need
244 to change the message type and/or the subscription ID in the message prior to using the reply function.
245 &space
246
247 To provide for both situations, two reply functions are supported by the Message object as illustrated
248 with the following prototypes.
249 &half_space
250
251 &ex_start
252    bool Send_response(  int mtype, int subid, int response_len,
253         std:shared_ptr<unsigned char> response );
254
255    bool Send_response(  int response_len, std::shared_ptr<unsigned char> response );
256 &ex_end
257 &fig_cen( Reply function prototypes. )
258 &space
259
260 In the first prototype the xApp must supply the new message type and subscription ID values, where the
261 second function uses the values which are currently set in the message.
262 Further, the new payload contents, and length, are supplied to both functions; the framework ensures
263 that the message is large enough to accommodate the payload, reallocating it if necessary, and copies
264 the response into the message payload prior to sending.
265 Should the xApp need to change either the message type, or the subscription ID, but not both, the &cw(NO_CHANGE)
266 constant can be used as illustrated below.
267 &half_space
268
269 &ex_start
270     msg->Send_response( xapp::Message::NO_CHANGE, xapp::Message::NO_SUBID,
271         pl_length, (unsigned char *) payload );
272 &ex_end
273 &fig_cen( Send response prototype. )
274 &space
275
276 In addition to the two function prototypes for &cw(Send_response()) there are two additional prototypes
277 which allow the new payload to be supplied as a shared smart pointer.
278 The other parameters to these functions are identical to those illustrated above, and thus are not presented
279 here.
280 &space
281
282 The &cw(Send_msg()) set of functions supported by the Message object are identical to the &cw(Send_response())
283 functions and are shown below.
284 &half_space
285
286 &ex_start
287     bool Send_msg( int mtype, int subid, int payload_len,
288         std::shared_ptr<unsigned char> payload );
289
290     bool Send_msg( int mtype, int subid, int payload_len,
291         unsigned char* payload );
292
293     bool Send_msg( int payload_len,
294         std::shared_ptr<unsigned char> payload );
295
296     bool Send_msg( int payload_len, unsigned char* payload );
297 &ex_end
298 &fig_cen( Send function prototypes. )
299 &space
300
301 Each send function accepts the message, copies in the payload provided, sets the message type and subscription
302 ID (if provided), and then causes the message to be sent.
303 The only difference between the &cw(Send_msg()) and &cw(Send_response()) functions is that the destination
304 of the message is selected based on the mapping of the message type and subscription ID using the current
305 routing table known to RMR.
306
307 &h2(Direct Payload Manipulation)
308 For some applications, it might be more efficient to manipulate the payload portion of an Xapp Message
309 in place, rather than creating it and relying on a buffer copy when the message is finally sent.
310 To achieve this, the xApp must either use the smart pointer to the payload passed to the callback function,
311 or retrieve one from the message using &cw(Get_payload()) when working with a message outside of a
312 callback function.
313 Once the smart pointer is obtained, the pointer's get() function can be used to directly reference the
314 payload (unsigned char) bytes.
315 &space
316
317 When working directly with the payload, the xApp must take care not to write more than the actual payload
318 size which can be extracted from the Message object using the &cw(Get_available_size()) function.
319 &space
320
321 When sending a message where the payload has been directly altered, and no extra buffer copy is needed,
322 a NULL pointer should be passed to the Message send function.
323 The following illustrates how the payload can be directly manipulated and returned to the sender (for
324 simplicity, there is no error handling if the payload size of the received message isn't large enough
325 for the response string, the response is just not sent).
326 &half_space
327
328 &ex_start
329     Msg_component payload;  // smart reference
330     int pl_size;            // max size of payload
331
332     payload = msg->Get_payload();
333     pl_size = msg->Get_available_size();
334     if( snprintf( (char *) payload.get(), pl_size,
335         "Msg Received\n" ) < pl_size ) {
336       msg->Send_response( M_TYPE, SID, strlen( raw_pl ), NULL );
337     }
338 &ex_end
339 &fig_cen( Send message without buffer copy. )
340 &space
341
342 &h2(Sending Multiple Responses)
343 It is likely that the xApp will wish to send multiple responses back to the process that sent a message
344 that triggered the callback.
345 The callback function may invoke the &cw(Send_response()) function multiple times before returning.
346 &space
347
348 After each call, the Message retains the necessary information to allow for a subsequent invocation to
349 send more data.
350 It should be noted though, that after the first call to &cw({Send_response()) the original payload
351 will be lost; if necessary, the xApp must make a copy of the payload before the first response call is
352 made.
353
354 &h2(Message Allocation)
355 Not all xApps will be "responders," meaning that some xApps will need to send one or more messages before
356 they can expect to receive any messages back.
357 To accomplish this, the xApp must first allocate a message buffer, optionally initialising the payload,
358 and then using the message's &cw(Send_msg()) function to send a message out.
359 The framework's &cw(Alloc_msg()) function can be used to create a Message object with a desired payload
360 size.
361
362 &h1(Framework Provided Callbacks)
363 The framework itself may provide message handling via the driver such that the xApp might not need to
364 implement some message processing functionality.
365 Initially, the C++ framework will provide a default callback function to handle the RMR based health
366 check messages.
367 This callback function will assume that if the message was received, and the callback invoked, that all
368 is well and will reply with an OK state.
369 If the xApp should need to override this simplistic response, all it needs to do is to register its
370 own callback function for the health check message type.