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