blob: d9137a3a273da24817d6bd83c6c72b2ae98dc52b [file] [log] [blame]
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001============
2USER'S GUIDE
3============
E. Scott Daniels97204c82020-06-29 15:39:57 -04004.. This work is licensed under a Creative Commons Attribution 4.0 International License.
5.. SPDX-License-Identifier: CC-BY-4.0
6..
7.. CAUTION: this document is generated from source in doc/src/*
8.. To make changes edit the source and recompile the document.
9.. Do NOT make changes directly to .rst or .md files.
10
11
E. Scott Daniels97204c82020-06-29 15:39:57 -040012
13
14INTRODUCTION
15============
16
E. Scott Danielsd486a172020-07-29 12:39:54 -040017The C++ framework allows the programmer to create an instance
18of the ``Xapp`` object which then can be used as a foundation
19for the application. The ``Xapp`` object provides a message
20level interface to the RIC Message Router (RMR), including
21the ability to register callback functions which the instance
22will drive as messages are received; much in the same way
23that an X-windows application is driven by the window manager
24for all activity. The xApp may also choose to use its own
25send/receive loop, and thus is not required to use the
26callback driver mechanism provided by the framework.
27
28
29Termonology
30-----------
31
32To avoid confusion the term **xAPP** is used in this document
33to refer to the user's application code which is creating
34``Xapp,`` and related objects provided by the *framework.*
35The use of *framework* should be taken to mean any of the
36classes and/or support functions which are provided by the
37``ricxfcpp`` library.
E. Scott Daniels97204c82020-06-29 15:39:57 -040038
39
40THE FRAMEWORK API
41=================
42
43The C++ framework API consists of the creation of the xApp
44object, and invoking desired functions via the instance of
45the object. The following paragraphs cover the various steps
46involved to create an xApp instance, wait for a route table
47to arrive, send a message, and wait for messages to arrive.
48
49
E. Scott Daniels6ef23e12020-07-15 08:03:22 -040050The Namespace
51-------------
52
53Starting with version 2.0.0 the framwork introduces a
54*namespace* of ``xapp`` for the following classes and types:
55
56
57 * Alarm
58 * Jhash
59 * Message
60 * Msg_component
61
62
63This is a breaking change and as such the major version was
64bumpped from 1 to 2.
65
66
E. Scott Daniels97204c82020-06-29 15:39:57 -040067Creating the xApp instance
68--------------------------
69
70The creation of the xApp instance is as simple as invoking
71the object's constructor with two required parameters:
72
73
E. Scott Daniels6ef23e12020-07-15 08:03:22 -040074 .. list-table::
75 :widths: auto
76 :header-rows: 0
77 :class: borderless
E. Scott Daniels97204c82020-06-29 15:39:57 -040078
E. Scott Danielsd486a172020-07-29 12:39:54 -040079
E. Scott Daniels6ef23e12020-07-15 08:03:22 -040080 * - **port**
E. Scott Danielsd486a172020-07-29 12:39:54 -040081
E. Scott Daniels6ef23e12020-07-15 08:03:22 -040082 -
E. Scott Danielsd486a172020-07-29 12:39:54 -040083
E. Scott Daniels6ef23e12020-07-15 08:03:22 -040084 A C string (pointer to char) which defines the port that
E. Scott Danielsd486a172020-07-29 12:39:54 -040085
E. Scott Daniels6ef23e12020-07-15 08:03:22 -040086 RMR will open to listen for connections.
E. Scott Daniels97204c82020-06-29 15:39:57 -040087
88
E. Scott Danielsd486a172020-07-29 12:39:54 -040089
90
91
E. Scott Daniels6ef23e12020-07-15 08:03:22 -040092 |
E. Scott Daniels97204c82020-06-29 15:39:57 -040093
E. Scott Danielsd486a172020-07-29 12:39:54 -040094
95
E. Scott Daniels6ef23e12020-07-15 08:03:22 -040096 * - **wait**
E. Scott Danielsd486a172020-07-29 12:39:54 -040097
E. Scott Daniels6ef23e12020-07-15 08:03:22 -040098 -
E. Scott Danielsd486a172020-07-29 12:39:54 -040099
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400100 A Boolean value which indicates whether or not the
E. Scott Danielsd486a172020-07-29 12:39:54 -0400101
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400102 initialization process should wait for the arrival of a
E. Scott Danielsd486a172020-07-29 12:39:54 -0400103
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400104 valid route table before completing. When true is
E. Scott Danielsd486a172020-07-29 12:39:54 -0400105
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400106 supplied, the initialization will not complete until RMR
E. Scott Danielsd486a172020-07-29 12:39:54 -0400107
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400108 has received a valid route table (or one is located via
E. Scott Danielsd486a172020-07-29 12:39:54 -0400109
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400110 the ``RMR_SEED_RT`` environment variable).
E. Scott Daniels97204c82020-06-29 15:39:57 -0400111
112
E. Scott Daniels97204c82020-06-29 15:39:57 -0400113
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400114The following code sample illustrates the simplicity of
115creating the instance of the xApp object.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400116
117
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400118::
119
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400120 #include <memory>
121 #include <ricxfcpp/xapp.hpp>
122 int main( ) {
123 std::unique_ptr<Xapp> xapp;
124 char* listen_port = (char *) "4560"; //RMR listen port
125 bool wait4table = true; // wait for a route table
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400126
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400127 xapp = std::unique_ptr<Xapp>(
128 new Xapp( listen_port, wait4table ) );
129 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400130
131Figure 1: Creating an xAPP instance.
132
133From a compilation perspective, the following is the simple
134compiler invocation string needed to compile and link the
135above program (assuming that the sample code exists in a file
136called ``man_ex1.cpp``.
137
138
139::
140
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400141 g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400142
143
144The above program, while complete and capable of being
145compiled, does nothing useful. When invoked, RMR will be
146initialized and will begin listening for a route table;
147blocking the return to the main program until one is
148received. When a valid route table arrives, initialization
149will complete and the program will exit as there is no code
150following the instruction to create the object.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400151
152
153LISTENING FOR MESSAGES
154======================
155
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400156The program in the previous example can be extended with just
157a few lines of code to enable it to receive and process
158messages. The application needs to register a callback
159function for each message type which it desires to process.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400160
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400161Once registered, each time a message is received the
162registered callback for the message type will be invoked by
163the framework.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400164
165
166Callback Signature
167------------------
168
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400169As with most callback related systems, a callback must have a
170well known function signature which generally passes event
171related information and a "user" data pointer which was
172registered with the function. The following is the prototype
173which callback functions must be defined with:
E. Scott Daniels97204c82020-06-29 15:39:57 -0400174
175
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400176::
E. Scott Daniels97204c82020-06-29 15:39:57 -0400177
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400178 void cb_name( xapp::Message& m, int mtype, int subid,
179 int payload_len, xapp::Msg_component payload,
180 void* usr_data );
E. Scott Daniels97204c82020-06-29 15:39:57 -0400181
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400182Figure 2: Callback function signature
E. Scott Daniels97204c82020-06-29 15:39:57 -0400183
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400184The parameters passed to the callback function are as
185follows:
E. Scott Daniels97204c82020-06-29 15:39:57 -0400186
E. Scott Daniels97204c82020-06-29 15:39:57 -0400187
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400188 .. list-table::
189 :widths: auto
190 :header-rows: 0
191 :class: borderless
E. Scott Daniels97204c82020-06-29 15:39:57 -0400192
E. Scott Danielsd486a172020-07-29 12:39:54 -0400193
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400194 * - **m**
E. Scott Danielsd486a172020-07-29 12:39:54 -0400195
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400196 -
E. Scott Danielsd486a172020-07-29 12:39:54 -0400197
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400198 A reference to the Message that was received.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400199
E. Scott Daniels97204c82020-06-29 15:39:57 -0400200
E. Scott Danielsd486a172020-07-29 12:39:54 -0400201
202
203
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400204 |
E. Scott Daniels97204c82020-06-29 15:39:57 -0400205
E. Scott Danielsd486a172020-07-29 12:39:54 -0400206
207
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400208 * - **mtype**
E. Scott Danielsd486a172020-07-29 12:39:54 -0400209
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400210 -
E. Scott Danielsd486a172020-07-29 12:39:54 -0400211
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400212 The message type (allows for disambiguation if the
E. Scott Danielsd486a172020-07-29 12:39:54 -0400213
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400214 callback is registered for multiple message types).
E. Scott Daniels97204c82020-06-29 15:39:57 -0400215
E. Scott Daniels97204c82020-06-29 15:39:57 -0400216
E. Scott Danielsd486a172020-07-29 12:39:54 -0400217
218
219
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400220 |
E. Scott Daniels97204c82020-06-29 15:39:57 -0400221
E. Scott Danielsd486a172020-07-29 12:39:54 -0400222
223
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400224 * - **subid**
E. Scott Danielsd486a172020-07-29 12:39:54 -0400225
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400226 -
E. Scott Danielsd486a172020-07-29 12:39:54 -0400227
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400228 The subscription ID from the message.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400229
E. Scott Daniels97204c82020-06-29 15:39:57 -0400230
E. Scott Danielsd486a172020-07-29 12:39:54 -0400231
232
233
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400234 |
E. Scott Daniels97204c82020-06-29 15:39:57 -0400235
E. Scott Danielsd486a172020-07-29 12:39:54 -0400236
237
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400238 * - **payload len**
E. Scott Danielsd486a172020-07-29 12:39:54 -0400239
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400240 -
E. Scott Danielsd486a172020-07-29 12:39:54 -0400241
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400242 The number of bytes which the sender has placed into the
E. Scott Danielsd486a172020-07-29 12:39:54 -0400243
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400244 payload.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400245
E. Scott Daniels97204c82020-06-29 15:39:57 -0400246
E. Scott Danielsd486a172020-07-29 12:39:54 -0400247
248
249
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400250 |
E. Scott Daniels97204c82020-06-29 15:39:57 -0400251
E. Scott Danielsd486a172020-07-29 12:39:54 -0400252
253
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400254 * - **payload**
E. Scott Danielsd486a172020-07-29 12:39:54 -0400255
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400256 -
E. Scott Danielsd486a172020-07-29 12:39:54 -0400257
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400258 A direct reference (smart pointer) to the payload. (The
E. Scott Danielsd486a172020-07-29 12:39:54 -0400259
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400260 smart pointer is wrapped in a special class in order to
E. Scott Danielsd486a172020-07-29 12:39:54 -0400261
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400262 provide a custom destruction function without burdening
E. Scott Danielsd486a172020-07-29 12:39:54 -0400263
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400264 the xApp developer with that knowledge.)
E. Scott Daniels97204c82020-06-29 15:39:57 -0400265
E. Scott Daniels97204c82020-06-29 15:39:57 -0400266
E. Scott Danielsd486a172020-07-29 12:39:54 -0400267
268
269
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400270 |
E. Scott Daniels97204c82020-06-29 15:39:57 -0400271
E. Scott Danielsd486a172020-07-29 12:39:54 -0400272
273
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400274 * - **user data**
E. Scott Danielsd486a172020-07-29 12:39:54 -0400275
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400276 -
E. Scott Danielsd486a172020-07-29 12:39:54 -0400277
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400278 A pointer to user data. This is the pointer that was
E. Scott Danielsd486a172020-07-29 12:39:54 -0400279
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400280 provided when the function was registered.
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400281
282
283
284To illustrate the use of a callback function, the previous
285code example has been extended to add the function, register
286it for message types 1000 and 1001, and to invoke the
287``Run()`` function in the framework (explained in the next
288section).
289
290::
291
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400292 #include <memory>
293 #include <ricxfcpp/xapp.hpp>
294 long m1000_count = 0; // message counters, one for each type
295 long m1001_count = 0;
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400296
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400297 // callback function that will increase the appropriate counter
298 void cbf( xapp::Message& mbuf, int mtype, int subid, int len,
299 xapp::Msg_component payload, void* data ) {
300 long* counter;
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400301
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400302 if( (counter = (long *) data) != NULL ) {
303 (*counter)++;
304 }
305 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400306
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400307 int main( ) {
308 std::unique_ptr<Xapp> xapp;
309 char* listen_port = (char *) "4560";
310 bool wait4table = false;
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400311
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400312 xapp = std::unique_ptr<Xapp>(
313 new Xapp( listen_port, wait4table ) );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400314
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400315 // register the same callback function for both msg types
316 xapp->Add_msg_cb( 1000, cbf, (void *) &m1000_count );
317 xapp->Add_msg_cb( 1001, cbf, (void *) &m1001_count );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400318
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400319 xapp->Run( 1 ); // start the callback driver
320 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400321
322Figure 3: Callback function example.
323
324As before, the program does nothing useful, but now it will
325execute and receive messages. For this example, the same
326function can be used to increment the appropriate counter
327simply by providing a pointer to the counter as the user data
328when the callback function is registered. In addition, a
329subtle change from the previous example has been to set the
330wait for table flag to ``false.``
331
332For an xApp that is a receive only application (never sends)
333it is not necessary to wait for RMR to receive a table from
334the Route Manager.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400335
336
337Registering A Default Callback
338------------------------------
339
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400340The xApp may also register a default callback function such
341that the function will be invoked for any message that does
342not have a registered callback. If the xAPP does not register
343a default callback, any message which cannot be mapped to a
344known callback function is silently dropped. A default
345callback is registered by providing a *generic* message type
346of ``xapp->DEFAULT_CALLBACK`` on an ``Add_msg_cb`` call.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400347
348
349The Framework Callback Driver
350-----------------------------
351
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400352The ``Run()`` function within the Xapp object is invoked to
353start the callback driver, and the xApp should not expect the
354function to return under most circumstances. The only
355parameter that the ``Run()`` function expects is the number
356of threads to start. For each thread requested, the framework
357will start a listener thread which will allow received
358messages to be processed in parallel. If supplying a value
359greater than one, the xApp must ensure that the callback
360functions are thread safe as it is very likely that the same
361callback function will be invoked concurrently from multiple
362threads.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400363
364
365SENDING MESSAGES
366================
367
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400368It is very likely that most xApps will need to send messages
369and will not operate in "receive only" mode. Sending the
370message is a function of the message object itself and can
371take one of two forms:
E. Scott Daniels97204c82020-06-29 15:39:57 -0400372
373
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400374 * Replying to the sender of a received message
E. Scott Daniels97204c82020-06-29 15:39:57 -0400375
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400376 * Sending a message (routed based on the message type and
377 subscription ID)
E. Scott Daniels97204c82020-06-29 15:39:57 -0400378
379
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400380When replying to the sender, the message type and
381subscription ID are not used to determine the destination of
382the message; RMR ensures that the message is sent back to the
383originating xApp. The xApp may still need to change the
384message type and/or the subscription ID in the message prior
385to using the reply function.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400386
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400387To provide for both situations, two reply functions are
388supported by the Message object as illustrated with the
389following prototypes.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400390
391
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400392::
E. Scott Daniels97204c82020-06-29 15:39:57 -0400393
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400394 bool Send_response( int mtype, int subid, int response_len,
395 std:shared_ptr<unsigned char> response );
E. Scott Daniels97204c82020-06-29 15:39:57 -0400396
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400397 bool Send_response( int response_len, std::shared_ptr<unsigned char> response );
E. Scott Daniels97204c82020-06-29 15:39:57 -0400398
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400399Figure 4: Reply function prototypes.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400400
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400401In the first prototype the xApp must supply the new message
402type and subscription ID values, where the second function
403uses the values which are currently set in the message.
404Further, the new payload contents, and length, are supplied
405to both functions; the framework ensures that the message is
406large enough to accommodate the payload, reallocating it if
407necessary, and copies the response into the message payload
408prior to sending. Should the xApp need to change either the
409message type, or the subscription ID, but not both, the
410``NO_CHANGE`` constant can be used as illustrated below.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400411
412
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400413::
E. Scott Daniels97204c82020-06-29 15:39:57 -0400414
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400415 msg->Send_response( xapp::Message::NO_CHANGE, xapp::Message::NO_SUBID,
416 pl_length, (unsigned char *) payload );
E. Scott Daniels97204c82020-06-29 15:39:57 -0400417
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400418Figure 5: Send response prototype.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400419
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400420In addition to the two function prototypes for
421``Send_response()`` there are two additional prototypes which
422allow the new payload to be supplied as a shared smart
423pointer. The other parameters to these functions are
424identical to those illustrated above, and thus are not
425presented here.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400426
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400427The ``Send_msg()`` set of functions supported by the Message
428object are identical to the ``Send_response()`` functions and
429are shown below.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400430
431
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400432::
E. Scott Daniels97204c82020-06-29 15:39:57 -0400433
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400434 bool Send_msg( int mtype, int subid, int payload_len,
435 std::shared_ptr<unsigned char> payload );
E. Scott Daniels97204c82020-06-29 15:39:57 -0400436
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400437 bool Send_msg( int mtype, int subid, int payload_len,
438 unsigned char* payload );
E. Scott Daniels97204c82020-06-29 15:39:57 -0400439
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400440 bool Send_msg( int payload_len,
441 std::shared_ptr<unsigned char> payload );
E. Scott Daniels97204c82020-06-29 15:39:57 -0400442
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400443 bool Send_msg( int payload_len, unsigned char* payload );
E. Scott Daniels97204c82020-06-29 15:39:57 -0400444
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400445Figure 6: Send function prototypes.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400446
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400447Each send function accepts the message, copies in the payload
448provided, sets the message type and subscription ID (if
449provided), and then causes the message to be sent. The only
450difference between the ``Send_msg()`` and
451``Send_response()`` functions is that the destination of the
452message is selected based on the mapping of the message type
453and subscription ID using the current routing table known to
454RMR.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400455
456
457Direct Payload Manipulation
458---------------------------
459
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400460For some applications, it might be more efficient to
461manipulate the payload portion of an Xapp Message in place,
462rather than creating it and relying on a buffer copy when the
463message is finally sent. To achieve this, the xApp must
464either use the smart pointer to the payload passed to the
465callback function, or retrieve one from the message using
466``Get_payload()`` when working with a message outside of a
467callback function. Once the smart pointer is obtained, the
468pointer's get() function can be used to directly reference
469the payload (unsigned char) bytes.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400470
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400471When working directly with the payload, the xApp must take
472care not to write more than the actual payload size which can
473be extracted from the Message object using the
474``Get_available_size()`` function.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400475
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400476When sending a message where the payload has been directly
477altered, and no extra buffer copy is needed, a NULL pointer
478should be passed to the Message send function. The following
479illustrates how the payload can be directly manipulated and
480returned to the sender (for simplicity, there is no error
481handling if the payload size of the received message isn't
482large enough for the response string, the response is just
483not sent).
E. Scott Daniels97204c82020-06-29 15:39:57 -0400484
485
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400486::
E. Scott Daniels97204c82020-06-29 15:39:57 -0400487
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400488 Msg_component payload; // smart reference
489 int pl_size; // max size of payload
E. Scott Daniels97204c82020-06-29 15:39:57 -0400490
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400491 payload = msg->Get_payload();
492 pl_size = msg->Get_available_size();
493 if( snprintf( (char *) payload.get(), pl_size,
494 "Msg Received\\n" ) < pl_size ) {
495 msg->Send_response( M_TYPE, SID, strlen( raw_pl ), NULL );
496 }
E. Scott Daniels97204c82020-06-29 15:39:57 -0400497
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400498Figure 7: Send message without buffer copy.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400499
500
501
502Sending Multiple Responses
503--------------------------
504
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400505It is likely that the xApp will wish to send multiple
506responses back to the process that sent a message that
507triggered the callback. The callback function may invoke the
508``Send_response()`` function multiple times before returning.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400509
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400510After each call, the Message retains the necessary
511information to allow for a subsequent invocation to send more
512data. It should be noted though, that after the first call to
513``{Send_response()`` the original payload will be lost; if
514necessary, the xApp must make a copy of the payload before
515the first response call is made.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400516
517
518Message Allocation
519------------------
520
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400521Not all xApps will be "responders," meaning that some xApps
522will need to send one or more messages before they can expect
523to receive any messages back. To accomplish this, the xApp
524must first allocate a message buffer, optionally initialising
525the payload, and then using the message's ``Send_msg()``
526function to send a message out. The framework's
527``Alloc_msg()`` function can be used to create a Message
528object with a desired payload size.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400529
530
531FRAMEWORK PROVIDED CALLBACKS
532============================
533
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400534The framework itself may provide message handling via the
535driver such that the xApp might not need to implement some
536message processing functionality. Initially, the C++
537framework will provide a default callback function to handle
538the RMR based health check messages. This callback function
539will assume that if the message was received, and the
540callback invoked, that all is well and will reply with an OK
541state. If the xApp should need to override this simplistic
542response, all it needs to do is to register its own callback
543function for the health check message type.
544
545
546JSON SUPPORT
547============
548
549The C++ xAPP framework provides a very lightweight json
550parser and data hash facility. Briefly, a json hash (Jhash)
551can be established by creating an instance of the Jhash
552object with a string of valid json. The resulting object's
553functions can then be used to read values from the resulting
554hash.
555
556
557Creating The Jhash Object
558-------------------------
559
560The Jhash object is created simply by passing a json string
561to the constructor.
562
563::
564
E. Scott Danielsd486a172020-07-29 12:39:54 -0400565 #include <ricxfcpp/Jhash.hpp>
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400566
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400567 std::string jstring = "{ \\"tag\\": \\"Hello World\\" }";
568 Jhash* jh;
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400569
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400570 jh = new Jhash( jstring.c_str() );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400571
572Figure 8: The creation of the Jhash object.
573
574Once the Jhash object has been created any of the methods
575described in the following paragraphs can be used to retrieve
576the data:
577
578
579Json Blobs
580----------
581
582Json objects can be nested, and the nesting is supported by
583this representation. The approach taken by Jhash is a
584"directory view" approach, where the "current directory," or
585current *blob,* limits the scope of visible fields.
586
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400587As an example, the json contained in figure 9, contains a
588"root" blob and two *sub-blobs* (address and lease_info).
589
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400590
591::
592
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400593 {
594 "lodge_name": "Water Buffalo Lodge 714",
595 "member_count": 41,
596 "grand_poobah": "Larry K. Slate",
597 "attendance": [ 23, 14, 41, 38, 24 ],
598 "address": {
599 "street": "16801 Stonway Lane",
600 "suite": null,
601 "city": "Bedrock",
602 "post_code": "45701"
603 },
604 "lease_info": {
605 "owner": "Stonegate Properties",
606 "amount": 216.49,
607 "due": "monthly",
608 "contact:" "Kyle Limestone"
609 }
610 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400611
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400612Figure 9: Sample json with a root and two blobs.
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400613
614Upon creation of the Jhash object, the *root* fields,
615``lodge_name,`` ``member_count,`` and ``grand_poobah`` are
616immediately available. The fields in the *sub-blobs* are
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400617available only when the correct blob is selected. The code
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400618sample in figure 10 illustrates how a *sub-blob* is selected.
619
620::
621
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400622 jh->Set_blob( (char *) "address" ); // select address
623 jh->Unset_blob(); // return to root
624 jh->Set_blob( (char *) "lease_info" ); // select the lease blob
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400625
626Figure 10: Blob selection example.
627
628Currently, the selected blob must be unset in order to select
629a blob at the root level; unset always sets the root blob.
630Attempting to use the ``Set_blob`` function will attempt to
631select the named blob from the current blob, and not the
632root.
633
634
635Simple Value Extraction
636-----------------------
637
638Simple values are the expected data types *string, value,*
639and *boolean.* This lightweight json parser treats all values
640as floating point numbers and does not attempt to maintain a
641separate integer type. A fourth type, *null,* is supported to
642allow the user to expressly check for a field which is
643defined but has no value; as opposed to a field that was
644completely missing from the data. The following are the
645prototypes for the functions which allow values to be
646extracted:
647
648
649::
650
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400651 std::string String( const char* name );
652 float Value( const char* name );
653 bool Bool( const char* name );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400654
655
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400656Each of these functions returns the value associated with the
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400657field with the given *name.* If the value is missing, the
658following default values are returned:
659
660
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400661 .. list-table::
662 :widths: 15,80
663 :header-rows: 0
664 :class: borderless
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400665
E. Scott Danielsd486a172020-07-29 12:39:54 -0400666
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400667 * - **String**
E. Scott Danielsd486a172020-07-29 12:39:54 -0400668
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400669 -
E. Scott Danielsd486a172020-07-29 12:39:54 -0400670
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400671 An empty string (.e.g "").
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400672
E. Scott Danielsd486a172020-07-29 12:39:54 -0400673
674
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400675 |
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400676
E. Scott Danielsd486a172020-07-29 12:39:54 -0400677
678
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400679 * - **Value**
E. Scott Danielsd486a172020-07-29 12:39:54 -0400680
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400681 -
E. Scott Danielsd486a172020-07-29 12:39:54 -0400682
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400683 Zero (e.g 0.0)
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400684
E. Scott Danielsd486a172020-07-29 12:39:54 -0400685
686
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400687 |
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400688
E. Scott Danielsd486a172020-07-29 12:39:54 -0400689
690
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400691 * - **bool**
E. Scott Danielsd486a172020-07-29 12:39:54 -0400692
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400693 -
E. Scott Danielsd486a172020-07-29 12:39:54 -0400694
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400695 false
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400696
697
698
699If the user needs to disambiguate between a missing value and
700the default value either the ``Missing`` or ``Exists``
701function should be used first.
702
703
704Testing For Existing and Missing Fields
705---------------------------------------
706
707Two functions allow the developer to determine whether or not
708a field is included in the json. Both of these functions work
709on the current *blob,* therefore it is important to ensure
710that the correct blob is selected before using either of
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400711these functions. The prototypes for the ``Exists`` and
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400712``Missing`` functions are below:
713
714::
715
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400716 bool Exists( const char* name );
717 bool Is_missing( const char* name );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400718
719The ``Exists`` function returns *true* if the field name
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400720exists in the json and *false* otherwise. Conversely, the
721``Missing`` function returns *true* when the field name does
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400722not exist in the json.
723
724
725Testing Field Type
726------------------
727
728The ``Exists`` and ``Missing`` functions might not be enough
729for the user code to validate the data that it has. To assist
730with this, several functions allow direct type testing on a
731field in the current blob. The following are the prototypes
732for these functions:
733
734::
735
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400736 bool Is_bool( const char* name );
737 bool Is_null( const char* name );
738 bool Is_string( const char* name );
739 bool Is_value( const char* name );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400740
741
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400742Each of these functions return *true* if the field with the
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400743given name is of the type being tested for.
744
745
746Arrays
747------
748
749Arrays are supported in the same manner as simple field
750values with the addition of the need to supply an array index
751when fetching values from the object. In addition, there is a
752*length* function which can be used to determine the number
753of elements in the named array. The prototypes for the array
754based functions are below:
755
756::
757
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400758 int Array_len( const char* name );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400759
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400760 bool Is_bool_ele( const char* name, int eidx );
761 bool Is_null_ele( const char* name, int eidx );
762 bool Is_string_ele( const char* name, int eidx );
763 bool Is_value_ele( const char* name, int eidx );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400764
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400765 bool Bool_ele( const char* name, int eidx );
766 std::string String_ele( const char* name, int eidx );
767 float Value_ele( const char* name, int eidx );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400768
769
770For each of these functions the ``eidx`` is the zero based
771element index which is to be tested or selected.
772
773
774Arrays of Blobs
775---------------
776
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400777An array containing blobs, rather than simple field value
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400778pairs, the blob must be selected prior to using it, just as a
779sub-blob needed to be selected. The ``Set_blob_ele`` function
780is used to do this and has the following prototype:
781
782::
783
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400784 bool Set_blob_ele( const char* name, int eidx );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400785
786
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400787As with selecting a sub-blob, an unset must be performed
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400788before selecting the next blob. Figure 11 illustrates how
789these functions can be used to read and print values from the
790json in figure 12.
791
792::
793
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400794 "members": [
795 { "name": "Fred Flinstone", "member_num": 42 },
796 { "name": "Barney Rubble", "member_num": 48 },
797 { "name": "Larry K Slate", "member_num": 22 },
798 { "name": "Kyle Limestone", "member_num": 49 }
799 ]
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400800
801Figure 11: Json array containing blobs.
802
803
804::
805
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400806 std::string mname;
807 float mnum;
808 int len;
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400809
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400810 len = jh->Array_len( (char *) "members" );
811 for( i = 0; i < len; i++ ) {
812 jh->Set_blob_ele( (char *) "members", i ); // select blob
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400813
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400814 mname = jh->String( (char *) "name" ); // read values
815 mnum = jh->Value( (char *) "member_num" );
816 fprintf( stdout, "%s is member %d\\n", mname.c_str(), (int) mnum );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400817
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400818 jh->Unset_blob(); // back to root
819 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400820
821Figure 12: Code to process the array of blobs.
822
E. Scott Daniels97204c82020-06-29 15:39:57 -0400823
824
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400825ALARM MANAGER INTERFACE
826=======================
827
828The C++ framework provides an API which allows the xAPP to
829easily construct and generate alarm messages. Alarm messages
830are a special class of RMR message, allocated in a similar
831fashion as an RMR message through the framework's
832``Alloc_alarm()`` function.
833
834The API consists of the following function types:
835
836
837 .. list-table::
838 :widths: auto
839 :header-rows: 0
840 :class: borderless
841
E. Scott Danielsd486a172020-07-29 12:39:54 -0400842
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400843 * - **Raise**
E. Scott Danielsd486a172020-07-29 12:39:54 -0400844
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400845 -
E. Scott Danielsd486a172020-07-29 12:39:54 -0400846
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400847 Cause the alarm to be assigned a severity and and sent via
E. Scott Danielsd486a172020-07-29 12:39:54 -0400848
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400849 RMR message to the alarm collector process.
850
851
E. Scott Danielsd486a172020-07-29 12:39:54 -0400852
853
854
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400855 |
856
E. Scott Danielsd486a172020-07-29 12:39:54 -0400857
858
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400859 * - **Clear**
E. Scott Danielsd486a172020-07-29 12:39:54 -0400860
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400861 -
E. Scott Danielsd486a172020-07-29 12:39:54 -0400862
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400863 Cause a clear message to be sent to the alarm collector.
864
865
E. Scott Danielsd486a172020-07-29 12:39:54 -0400866
867
868
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400869 |
870
E. Scott Danielsd486a172020-07-29 12:39:54 -0400871
872
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400873 * - **Raise Again**
E. Scott Danielsd486a172020-07-29 12:39:54 -0400874
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400875 -
E. Scott Danielsd486a172020-07-29 12:39:54 -0400876
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400877 Cause a clear followed by a raise message to be sent to
E. Scott Danielsd486a172020-07-29 12:39:54 -0400878
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400879 the alarm collector.
880
881
882
883
884
885Allocating Alarms
886-----------------
887
888The ``xapp`` function provided by the framework is used to
889create an alarm object. Once the xAPP has an alarm object it
890can be used to send one, or more, alarm messages to the
891collector.
892
893The allocation function has three prototypes which allow the
894xAPP to create an alarm with an initial set of information as
895is appropriate. The following are the prototypes for the
896allocate functions:
897
898
899::
900
901 std::unique_ptr<xapp::Alarm> Alloc_alarm( );
902 std::unique_ptr<xapp::Alarm> Alloc_alarm( std::string meid );
903 std::unique_ptr<xapp::Alarm> Alloc_alarm( int prob_id, std::string meid );
904
905Figure 13: Alarm allocation prototypes.
906
907Each of the allocation functions returns a unique pointer to
908the alarm. In the simplest form (1) the alarm is initialised
909with an empty meid (managed element ID) string, and unset
910problem ID (-1). The second prototype allows the xAPP to
911supply the meid, and in the third form both the problem ID
912and the meid are used to initialise the alarm.
913
914
915Raising An Alarm
916----------------
917
918Once an alarm has been allocated, its ``Raise()`` function
919can be used to cause the alarm to be sent to the collector.
920The raise process allows the xAPP to perform the following
921modifications to the alarm before sending the message:
922
923
924 * Set the alarm severity
925
926 * Set the problem ID value
927
928 * Set the alarm information string
929
930 * Set the additional information string
931
932
933The following are the prototypes for the ``Raise()``
934functions of an alarm object: ..... In its simplest form (1)
935the ``Raise()`` function will send the alarm without making
936any changes to the data. The final two forms allow the xAPP
937to supply additional data which is added to the alarm before
938sending the message. Each of the raise functions returns
939``true`` on success and ``false`` if the alarm message could
940not be sent.
941
942
943Severity
944--------
945
946The severity is one of the ``SEV_`` constants listed below.
947These map to alarm collector strings and insulate the xAPP
948from any future alarm collector changes. The specific meaning
949of these severity types are defined by the alarm collector
950and thus no attempt is made to guess what their actual
951meaning is. These constants are available by including
952``alarm.hpp.``
953
954
955 ::
956
957 SEV_MAJOR
958 SEV_MINOR
959 SEV_WARN
960 SEV_DEFAULT
961
962Figure 14: Severity constants available in alarm.hpp.
963
964
965The Problem ID
966--------------
967
968The problem ID is an integer which is assigned by the xAPP.
969The framework makes no attempt to verify that it has been se,
970nor does it attempt to validate the value. If the xAPP does
971not set the value, ``-1`` is used.
972
973
974Information Strings
975-------------------
976
977The two information strings are also xAPP defined and provide
978the information that the xAPP deems necessary and related to
979the alarm. What the collector expects, and how these strings
980are used, is beyond the scope of the framework to describe or
981validate. If not supplied, empty strings are sent in the
982alarm message.
983
984
985Clearing An Alarm
986-----------------
987
988The ``Clear()`` function of an alarm may be used to send a
989clear message. In a manner similar to the ``Raise()``
990functions, the ``Clear()`` functions allow the existing alarm
991data to be sent without change, or for the xAPP to modify the
992data before the message is sent to the collector. The
993following are the prototype for these functions.
994
995::
996
997 bool Clear( );
998 bool Clear( int severity, int problem, std::string info );
999 bool Clear( int severity, int problem, std::string info, std::string addional_info );
1000 bool Clear_all( );
1001
1002
1003Figure 15: Clear function prototypes.
1004
1005Each of the clear functions returns ``true`` on success and
1006``false`` if the alarm message could not be sent.
1007
1008The ``Clear_all()`` function sends a special action code to
1009the collector which is assumed to clear all alarms. However,
1010it is unknown whether that implies **all** alarms, or all
1011alarms matching the ``problem_id,`` or some other
1012interpretation. Please consult the alarm collector
1013documentation for these specifics.
1014
1015
1016Adjusting Alarm Contents
1017------------------------
1018
1019It might be necessary for the xAPP to adjust the alarm
1020contents outside of the scope of the ``Raise()`` function, or
1021to adjust data that cannot be manipulated by ``Raise().`` The
1022following are the (self explanatory) prototypes for the
1023*setter* functions which are available to the xAPP.
1024
1025
1026::
1027
1028 void Set_additional( std::string new_info );
1029 void Set_appid( std::string new_id );
1030 void Set_info( std::string new_info );
1031 void Set_meid( std::string new_meid );
1032 void Set_problem( int new_id );
1033 void Set_severity( int new_sev );
1034
1035Figure 16: Alarm Setters
1036
1037
1038
E. Scott Danielsef362052020-07-22 15:49:54 -04001039METRICS SUPPORT
1040===============
1041
1042The C++ xAPP framework provides a lightweight interface to
1043the metrics gateway allowing the xAPP to create and send
1044metrics updates without needing to understand the underlying
1045message format. From the xAPP's perspective, the metrics
1046object is created with one or more key/value measurement
1047pairs and then is sent to the process responsible for
1048forwarding them to the various collection processes. The
1049following sections describe the Metrics object and the API
1050associated with it.
1051
1052
1053Creating The Metrics Object
1054---------------------------
1055
1056The ``xapp`` object can be created directly, or via the xapp
1057framework. When creating directly the xAPP must supply an RMR
1058message for the object to use; when the framework is used to
1059create the object, the message is created as as part of the
1060process. The framework provides three constructors for the
1061metrics instance allowing the xAPP to supply the measurement
1062source, the source and reporter, or to default to using the
1063xAPP name as both the source and reporter (see section
1064*Source and Reporter* for a more detailed description of
1065these). The framework constructors are illustrated in figure
106617.
1067
1068
1069::
1070
1071 std::unique_ptr<xapp::Metrics> Alloc_metrics( );
1072 std::unique_ptr<xapp::Metrics> Alloc_metrics( std::string source );
1073 std::unique_ptr<xapp::Metrics> Alloc_metrics( std::string reporter, std::string source );
1074
1075Figure 17: The framework constructors for creating an
1076instance of the metrics object.
1077
1078
1079::
1080
1081
1082 #include <ricxfcpp/Metrics>
1083
1084 char* port = (char *) "4560";
1085
1086 auto x = std::unique_ptr<Xapp>( new Xapp( port ) );
1087 auto reading = std::shared_ptr<xapp::Metrics>( x->Alloc_metric( ) );
1088
1089Figure 18: Metrics instance creation using the framework.
1090
1091Figures 18 illustrates how the framework constructor can be
1092used to create a metrics instance. While it is unlikely that
1093an xAPP will create a metrics instance directly, there are
1094three similar constructors available. These are prototypes
1095are shown in figure 19 and their use is illustrated in figure
109620.
1097
1098::
1099
1100 Metrics( std::shared_ptr<xapp::Message> msg );
1101 Metrics( std::shared_ptr<xapp::Message> msg, std::string msource );
1102 Metrics( std::shared_ptr<xapp::Message> msg, std::string reporter, std::string msource );
1103
1104Figure 19: Metrics object constructors.
1105
1106
1107::
1108
1109 #include <ricxfcpp/Metrics>
1110
1111 char* port = (char *) "4560";
1112
1113 auto x = std::unique_ptr<Xapp>( new Xapp( port ) );
1114 auto msg = std::shared_ptr<xapp::Message>( x->Alloc_msg( 4096 ) );
1115 auto reading = std::shared_ptr<xapp::Metrics>( new Metrics( msg ) );
1116
1117Figure 20: Direct creation of a metrics instance.
1118
1119
1120
1121Adding Values
1122-------------
1123
1124Once an instance of the metrics object is created, the xAPP
1125may push values in preparation to sending the measurement(s)
1126to the collector. The ``Push_data()`` function is used to
1127push each key/value pair and is illustrated in figure 21.
1128
1129::
1130
1131 reading->Push_data( "normal_count", (double) norm_count );
1132 reading->Push_data( "high_count", (double) hi_count );
1133 reading->Push_data( "excessive_count", (double) ex_count );
1134
1135Figure 21: Pushing key/value pairs into a metrics instance.
1136
1137
1138
1139Sending A Measurement Set
1140-------------------------
1141
1142After all of the measurement key/value pairs have been added
1143to the instance, the ``Send()`` function can be invoked to
1144create the necessary RMR message and send that to the
1145collection application. Following the send, the key/value
1146pairs are cleared from the instance and the xAPP is free to
1147start pushing values into the instance again. The send
1148function has the following prototype and returns ``true`` on
1149success and ``false`` if the measurements could not be sent.
1150
1151
1152Source and Reporter
1153-------------------
1154
1155The alarm collector has the understanding that a measurement
1156might be *sourced* from one piece of equipment, or software
1157component, but reported by another. For auditing purposes it
1158makes sense to distinguish these, and as such the metrics
1159object allows the xAPP to identify the case when the source
1160and reporter are something other than the xAPP which is
1161generating the metrics message(s).
1162
1163The *source* is the component to which the measurement
1164applies. This could be a network interface card counting
1165packets, a temperature sensor, or the xAPP itself reporting
1166xAPP related metrics. The *reporter* is the application that
1167is reporting the measurement(s) to the collector.
1168
1169By default, both reporter and source are assumed to be the
1170xAPP, and the name is automatically determined using the
1171run-time supplied programme name. Should the xAPP need to
1172report measurements for more than one source it has the
1173option to create an instance for every reporter source
1174combination, or to set the reporter and/or source with the
1175generation of each measurement set. To facilitate the ability
1176to change the source and/or the reporter without the need to
1177create a new metrics instance, two *setter* functions are
1178provided. The prototypes for these are shown in figure 22.
1179
1180
1181::
1182
1183 void Set_source( std::string new_source );
1184 void Set_reporter( std::string new_reporter );
1185
1186Figure 22: Setter functions allowing the reporter and/or
1187source to be set after construction.
1188
1189
1190
E. Scott Danielsd486a172020-07-29 12:39:54 -04001191CONFIGURATION SUPPORT
1192=====================
1193
1194The C++ xAPP framework provides the xAPP with an interface to
1195load, parse and receive update notifications on the
1196configuration. The configuration, also known as the xAPP
1197descriptor, is assumed to be a file containing json with a
1198well known structure, with some fields or *objects* used by
1199an xAPP for configuration purposes. The following paragraphs
1200describe the support that the framework provides to the xAPP
1201with respect to the configuration aspects of the descriptor.
1202
1203
1204The Config Object
1205-----------------
1206
1207The xAPP must create an instance of the ``config`` object in
1208order to take advantage of the support. This is accomplished
1209by using one of two constructors illustrated with code
1210samples in figure 23.
1211
1212
1213::
1214
1215 #include <ricxfcpp/config.hpp>
1216
1217 auto cfg = new xapp::Config( );
1218 auto cfg = new xapp::Config( "/var/myapp/config.json" );
1219
1220Figure 23: Creating a configuration instance.
1221
1222The creation of the object causes the file to be found,
1223loaded, after which the xAPP can use the instance functions
1224to access the information it needs.
1225
1226
1227Available Functions
1228-------------------
1229
1230Once a configuration has been created the following
1231capabilities are available:
1232
1233
1234 * Get a control value (numeric, string, or boolean)
1235
1236 * Get the RMR port for the container with the supplied
1237 name
1238
1239 * Set a notification callback function
1240
1241 * Get the raw contents of the file
1242
1243
1244
1245Control Values
1246--------------
1247
1248The ``controls`` section of the xAPP descriptor is generally
1249used to supply a *flat* namespace of key/value pairs which
1250the xAPP can use for configuration. These pairs are supplied
1251by the xAPP author as a part of development, and thus are
1252specific to each xAPP. The framework provides a general set
1253of functions which allows a key to be searched for in this
1254section and returned to the caller. Data is assumed to be one
1255of three types: numeric (double), string, or boolean.
1256
1257Two methods for each return type are supported with the more
1258specific form allowing the xAPP to supply a default value to
1259be used should the file not contain the requested field. The
1260function prototypes for these are provided in figure 24.
1261
1262::
1263
1264 bool Get_control_bool( std::string name, bool defval );
1265 bool Get_control_bool( std::string name );
1266
1267 std::string Get_control_str( std::string name, std::string defval );
1268 std::string Get_control_str( std::string name );
1269
1270 double Get_control_value( std::string name, double defval );
1271 double Get_control_value( std::string name );
1272
1273Figure 24: The various controls section get functions.
1274
1275If the more generic form of these functions is used, without
1276a default value, the return values are false, "", and 0.0 in
1277the respective order of the prototypes in figure 24.
1278
1279
1280The RMR Port
1281------------
1282
1283The ``messaging`` section of the xAPP descriptor provides the
1284ability to define one or more RMR *listen ports* that apply
1285to the xAPP(s) started in a given container. The xAPP may
1286read a port value (as a string) using the defined port name
1287via the ``Get_port`` function whose prototype is illustrated
1288in figure 25 below.
1289
1290
1291::
1292
1293 std::string Get_port( std::string name );
1294
1295Figure 25: The get port prototype.
1296
1297
1298
1299Raw File Contents
1300-----------------
1301
1302While it is not anticipated to be necessary, the xAPP might
1303need direct access to the raw contents of the configuration
1304file. As a convenience the framework provides the
1305``Get_contents()`` function which reads the entire file into
1306a standard library string and returns that to the calling
1307function. Parsing and interpreting the raw contents is then
1308up to the xAPP.
1309
1310
1311Notification Of Changes
1312-----------------------
1313
1314When desired, the xAPP may register a notification callback
1315function with the framework. This callback will be driven any
1316time a change to the descriptor is detected. When a change is
1317detected, the revised descriptor is read into the existing
1318object (overlaying any previous information), before invoking
1319the callback. The callback may then retrieve the updated
1320values and make any adjustments which are necessary. The
1321prototype for the xAPP callback function is described in
1322figure 26.
1323
1324
1325::
1326
1327 void cb_name( xapp::Config& c, void* data )
1328
1329Figure 26: The prototype which the xAPP configuration notify
1330callback must use.
1331
1332
1333
1334Enabling The Notifications
1335--------------------------
1336
1337Notifications are enabled by invoking the
1338``Set_callback()`` function. Once enabled, the framework will
1339monitor the configuration source and invoke the callback upon
1340change. This occurs in a separate thread than the main xAPP
1341thread; it is up to the xAPP to guard against any potential
1342data collisions when evaluating configuration changes. If the
1343xAPP does not register a notification function the framework
1344will not monitor the configuration for changes and the object
1345will have static data. Figure 27 illustrates how the xAPP can
1346define and register a notification callback.
1347
1348
1349::
1350
1351
1352 // notification callback; allows verbose level to change on the fly
1353 void config_chg( xapp::Config& c, void* vdata ) {
1354 app_ctx* ctx; // application context
1355
1356 ctx = (app_ctx *) vdata;
1357 ctx->vlevel = c->Get_value( "verbose_level", ctx->vlevel );
1358 }
1359
1360Figure 27: Small notification callback function allowing on
1361the fly verbose level change.
1362
1363
1364The xAPP would register the ``config_chg()`` function as the
1365notification callback using the call illustrated in figure
136628.
1367
1368::
1369
1370
1371 auto conf = new xapp::Config();
1372 conf->Set_callback( config_chg );
1373
1374Figure 28: Setting the notification callback and and
1375activating notifications.
1376
1377
1378
1379
1380xAPP Descriptor Notes
1381---------------------
1382
1383While it is beyond the scope of this document to describe the
1384complete contents of an xAPP descriptor file, it is prudent
1385to mention several items which are related to the information
1386used from the descriptor file. The following paragraphs
1387discuss things which the xAPP developer should be aware of,
1388and keep in mind when using the configuration class.
1389
1390
1391The RMR Section
1392---------------
1393
1394There is a deprecated section within the xAPP descriptor
1395which has the title *rmr.* The *messaging* section provides
1396more flexibility, and additional information and has been a
1397replacement for the *rmr* section for all applications. The
1398information in the *rmr* section should be kept consistent
1399with the duplicated information in the *messaging* section as
1400long as there are container management and/or platform
1401applications (e.g. Route Manager) which are back level and do
1402not recognise the *messaging* section. The configuration
1403parsing and support provided by the framework will ignore the
1404*rmr* section.
1405
1406
E. Scott Daniels97204c82020-06-29 15:39:57 -04001407EXAMPLE PROGRAMMES
1408==================
1409
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001410The following sections contain several example programmes
E. Scott Danielsd486a172020-07-29 12:39:54 -04001411which are written on top of the C++ framework. All of these
1412examples are available in the code repository RIC xAPP C++
1413framework available via the following URL:
1414
1415.. class:: center
1416 ``https://gerrit.o-ran-sc.org/r/admin/repos/ric-plt/xapp-frame-cpp``
1417
E. Scott Daniels97204c82020-06-29 15:39:57 -04001418
1419
1420RMR Dump xAPP
1421-------------
1422
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001423The RMR dump application is an example built on top of the
1424C++ xApp framework to both illustrate the use of the
1425framework, and to provide a useful diagnostic tool when
1426testing and troubleshooting xApps.
E. Scott Daniels97204c82020-06-29 15:39:57 -04001427
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001428The RMR dump xApp isn't a traditional xApp inasmuch as its
1429goal is to listen for message types and to dump information
1430about the messages received to the TTY much as
1431``tcpdump`` does for raw packet traffic. The full source
1432code, and Makefile, are in the ``examples`` directory of the
1433C++ framework repo.
E. Scott Daniels97204c82020-06-29 15:39:57 -04001434
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001435When invoked, the RMR dump program is given one or more
1436message types to listen for. A callback function is
1437registered for each, and the framework ``Run()`` function is
1438invoked to drive the process. For each recognised message,
1439and depending on the verbosity level supplied at program
1440start, information about the received message(s) is written
1441to the TTY. If the forwarding option, -f, is given on the
1442command line, and an appropriate route table is provided,
1443each received message is forwarded without change. This
1444allows for the insertion of the RMR dump program into a flow,
1445however if the ultimate receiver of a message needs to reply
1446to that message, the reply will not reach the original
1447sender, so RMR dump is not a complete "middle box"
1448application.
E. Scott Daniels97204c82020-06-29 15:39:57 -04001449
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001450The following is the code for this xAPP. Several functions,
1451which provide logic unrelated to the framework, have been
1452omitted. The full code is in the framework repository.
E. Scott Daniels97204c82020-06-29 15:39:57 -04001453
1454
1455
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001456 ::
E. Scott Daniels97204c82020-06-29 15:39:57 -04001457
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001458 #include <stdio.h>
1459 #include <unistd.h>
1460 #include <atomic>
E. Scott Daniels97204c82020-06-29 15:39:57 -04001461
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001462 #include "ricxfcpp/xapp.hpp"
E. Scott Daniels97204c82020-06-29 15:39:57 -04001463
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001464 /*
1465 Information that the callback needs outside
1466 of what is given to it via parms on a call
1467 by the framework.
1468 */
1469 typedef struct {
1470 int vlevel; // verbosity level
1471 bool forward; // if true, message is forwarded
1472 int stats_freq; // header/stats after n messages
1473 std::atomic<long> pcount; // messages processed
1474 std::atomic<long> icount; // messages ignored
1475 std::atomic<int> hdr; // number of messages before next header
1476 } cb_info_t;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001477
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001478 // ----------------------------------------------------------------------
E. Scott Daniels97204c82020-06-29 15:39:57 -04001479
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001480 /*
1481 Dump bytes to tty.
1482 */
1483 void dump( unsigned const char* buf, int len ) {
1484 int i;
1485 int j;
1486 char cheater[17];
E. Scott Daniels97204c82020-06-29 15:39:57 -04001487
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001488 fprintf( stdout, "<RD> 0000 | " );
1489 j = 0;
1490 for( i = 0; i < len; i++ ) {
1491 cheater[j++] = isprint( buf[i] ) ? buf[i] : '.';
1492 fprintf( stdout, "%02x ", buf[i] );
E. Scott Daniels97204c82020-06-29 15:39:57 -04001493
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001494 if( j == 16 ) {
1495 cheater[j] = 0;
1496 fprintf( stdout, " | %s\\n<RD> %04x | ", cheater, i+1 );
1497 j = 0;
1498 }
1499 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001500
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001501 if( j ) {
1502 i = 16 - (i % 16);
1503 for( ; i > 0; i-- ) {
1504 fprintf( stdout, " " );
1505 }
1506 cheater[j] = 0;
1507 fprintf( stdout, " | %s\\n", cheater );
1508 }
1509 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001510
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001511 /*
1512 generate stats when the hdr count reaches 0. Only one active
1513 thread will ever see it be exactly 0, so this is thread safe.
1514 */
1515 void stats( cb_info_t& cbi ) {
1516 int curv; // current stat trigger value
E. Scott Daniels97204c82020-06-29 15:39:57 -04001517
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001518 curv = cbi.hdr--;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001519
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001520 if( curv == 0 ) { // stats when we reach 0
1521 fprintf( stdout, "ignored: %ld processed: %ld\\n",
1522 cbi.icount.load(), cbi.pcount.load() );
1523 if( cbi.vlevel > 0 ) {
1524 fprintf( stdout, "\\n %5s %5s %2s %5s\\n",
1525 "MTYPE", "SUBID", "ST", "PLLEN" );
1526 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001527
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001528 cbi.hdr = cbi.stats_freq; // reset must be last
1529 }
1530 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001531
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001532 void cb1( xapp::Message& mbuf, int mtype, int subid, int len,
1533 xapp::Msg_component payload, void* data ) {
1534 cb_info_t* cbi;
1535 long total_count;
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001536
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001537 if( (cbi = (cb_info_t *) data) == NULL ) {
1538 return;
1539 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001540
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001541 cbi->pcount++;
1542 stats( *cbi ); // gen stats & header if needed
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001543
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001544 if( cbi->vlevel > 0 ) {
1545 fprintf( stdout, "<RD> %-5d %-5d %02d %-5d \\n",
1546 mtype, subid, mbuf.Get_state(), len );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001547
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001548 if( cbi->vlevel > 1 ) {
1549 dump( payload.get(), len > 64 ? 64 : len );
1550 }
1551 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001552
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001553 if( cbi->forward ) {
1554 // forward with no change to len or payload
1555 mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL );
1556 }
1557 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001558
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001559 /*
1560 registered as the default callback; it counts the
1561 messages that we aren't giving details about.
1562 */
1563 void cbd( xapp::Message& mbuf, int mtype, int subid, int len,
1564 xapp::Msg_component payload, void* data ) {
1565 cb_info_t* cbi;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001566
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001567 if( (cbi = (cb_info_t *) data) == NULL ) {
1568 return;
1569 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001570
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001571 cbi->icount++;
1572 stats( *cbi );
E. Scott Daniels97204c82020-06-29 15:39:57 -04001573
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001574 if( cbi->forward ) {
1575 // forward with no change to len or payload
1576 mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL );
1577 }
1578 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001579
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001580 int main( int argc, char** argv ) {
1581 std::unique_ptr<Xapp> x;
1582 char* port = (char *) "4560";
1583 int ai = 1; // arg processing index
1584 cb_info_t* cbi;
1585 int ncb = 0; // number of callbacks registered
1586 int mtype;
1587 int nthreads = 1;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001588
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001589 cbi = (cb_info_t *) malloc( sizeof( *cbi ) );
1590 cbi->pcount = 0;
1591 cbi->icount = 0;
1592 cbi->stats_freq = 10;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001593
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001594 ai = 1;
1595 // very simple flag parsing (no error/bounds checking)
1596 while( ai < argc ) {
1597 if( argv[ai][0] != '-' ) { // break on first non-flag
1598 break;
1599 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001600
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001601 // very simple arg parsing; each must be separate -x -y not -xy.
1602 switch( argv[ai][1] ) {
1603 case 'f': // enable packet forwarding
1604 cbi->forward = true;
1605 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001606
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001607 case 'p': // define port
1608 port = argv[ai+1];
1609 ai++;
1610 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001611
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001612 case 's': // stats frequency
1613 cbi->stats_freq = atoi( argv[ai+1] );
1614 if( cbi->stats_freq < 5 ) { // enforce sanity
1615 cbi->stats_freq = 5;
1616 }
1617 ai++;
1618 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001619
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001620 case 't': // thread count
1621 nthreads = atoi( argv[ai+1] );
1622 if( nthreads < 1 ) {
1623 nthreads = 1;
1624 }
1625 ai++;
1626 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001627
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001628 case 'v': // simple verbose bump
1629 cbi->vlevel++;
1630 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001631
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001632 case 'V': // explicit verbose level
1633 cbi->vlevel = atoi( argv[ai+1] );
1634 ai++;
1635 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001636
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001637 default:
1638 fprintf( stderr, "unrecognised option: %s\\n", argv[ai] );
1639 fprintf( stderr, "usage: %s [-f] [-p port] "
1640 "[-s stats-freq] [-t thread-count] "
1641 "[-v | -V n] msg-type1 ... msg-typen\\n",
1642 argv[0] );
1643 fprintf( stderr, "\\tstats frequency is based on # of messages received\\n" );
1644 fprintf( stderr, "\\tverbose levels (-V) 0 counts only, "
1645 "1 message info 2 payload dump\\n" );
1646 exit( 1 );
1647 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001648
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001649 ai++;
1650 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001651
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001652 cbi->hdr = cbi->stats_freq;
1653 fprintf( stderr, "<RD> listening on port: %s\\n", port );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001654
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001655 // create xapp, wait for route table if forwarding
1656 x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001657
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001658 // register callback for each type on the command line
1659 while( ai < argc ) {
1660 mtype = atoi( argv[ai] );
1661 ai++;
1662 fprintf( stderr, "<RD> capturing messages for type %d\\n", mtype );
1663 x->Add_msg_cb( mtype, cb1, cbi );
1664 ncb++;
1665 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001666
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001667 if( ncb < 1 ) {
1668 fprintf( stderr, "<RD> no message types specified on the command line\\n" );
1669 exit( 1 );
1670 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001671
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001672 x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi ); // register default cb
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001673
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001674 fprintf( stderr, "<RD> starting driver\\n" );
1675 x->Run( nthreads );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001676
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001677 // return from run() is not expected, but some compilers might
1678 // compilain if there isn't a return value here.
1679 return 0;
1680 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001681
E. Scott Danielsd486a172020-07-29 12:39:54 -04001682 Figure 29: Simple callback application.
E. Scott Daniels97204c82020-06-29 15:39:57 -04001683
1684
1685Callback Receiver
1686-----------------
1687
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001688This sample programme implements a simple message listener
1689which registers three callback functions to process two
1690specific message types and a default callback to handle
1691unrecognised messages.
E. Scott Daniels97204c82020-06-29 15:39:57 -04001692
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001693When a message of type 1 is received, it will send two
1694response messages back to the sender. Two messages are sent
1695in order to illustrate that it is possible to send multiple
1696responses using the same received message.
E. Scott Daniels97204c82020-06-29 15:39:57 -04001697
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001698The programme illustrates how multiple listening threads can
1699be used, but the programme is **not** thread safe; to keep
1700this example as simple as possible, the counters are not
1701locked when incremented.
E. Scott Daniels97204c82020-06-29 15:39:57 -04001702
1703
E. Scott Danielsef362052020-07-22 15:49:54 -04001704Metrics Generation
1705------------------
1706
1707The example also illustrates how a metrics object instance
1708can be created and used to send appliction metrics to the
1709collector. In this example the primary callback function will
1710genereate metrics with the receipt of each 1000th message.
1711
1712
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001713 ::
E. Scott Daniels97204c82020-06-29 15:39:57 -04001714
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001715 #include <stdio.h>
E. Scott Daniels97204c82020-06-29 15:39:57 -04001716
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001717 #include "ricxfcpp/message.hpp"
1718 #include "ricxfcpp/msg_component.hpp"
E. Scott Danielsef362052020-07-22 15:49:54 -04001719 #include <ricxfcpp/metrics.hpp>
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001720 #include "ricxfcpp/xapp.hpp"
E. Scott Daniels97204c82020-06-29 15:39:57 -04001721
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001722 // counts; not thread safe
1723 long cb1_count = 0;
1724 long cb2_count = 0;
1725 long cbd_count = 0;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001726
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001727 long cb1_lastts = 0;
1728 long cb1_lastc = 0;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001729
E. Scott Danielsef362052020-07-22 15:49:54 -04001730 /*
1731 Respond with 2 messages for each type 1 received
1732 Send metrics every 1000 messages.
1733 */
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001734 void cb1( xapp::Message& mbuf, int mtype, int subid, int len,
1735 xapp::Msg_component payload, void* data ) {
1736 long now;
1737 long total_count;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001738
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001739 // illustrate that we can use the same buffer for 2 rts calls
1740 mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK1\\n" );
1741 mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\\n" );
E. Scott Daniels97204c82020-06-29 15:39:57 -04001742
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001743 cb1_count++;
E. Scott Danielsef362052020-07-22 15:49:54 -04001744
1745 if( cb1_count % 1000 == 0 && data != NULL ) { // send metrics every 1000 messages
1746 auto x = (Xapp *) data;
1747 auto msgm = std::shared_ptr<xapp::Message>( x->Alloc_msg( 4096 ) );
1748
1749 auto m = std::unique_ptr<xapp::Metrics>( new xapp::Metrics( msgm ) );
1750 m->Push_data( "tst_cb1", (double) cb1_count );
1751 m->Push_data( "tst_cb2", (double) cb2_count );
1752 m->Send();
1753 }
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001754 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001755
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001756 // just count messages
1757 void cb2( xapp::Message& mbuf, int mtype, int subid, int len,
1758 xapp::Msg_component payload, void* data ) {
1759 cb2_count++;
1760 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001761
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001762 // default to count all unrecognised messages
1763 void cbd( xapp::Message& mbuf, int mtype, int subid, int len,
1764 xapp::Msg_component payload, void* data ) {
1765 cbd_count++;
1766 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001767
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001768 int main( int argc, char** argv ) {
1769 Xapp* x;
1770 char* port = (char *) "4560";
1771 int ai = 1; // arg processing index
1772 int nthreads = 1;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001773
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001774 // very simple flag processing (no bounds/error checking)
1775 while( ai < argc ) {
1776 if( argv[ai][0] != '-' ) {
1777 break;
1778 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001779
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001780 switch( argv[ai][1] ) { // we only support -x so -xy must be -x -y
1781 case 'p':
1782 port = argv[ai+1];
1783 ai++;
1784 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001785
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001786 case 't':
1787 nthreads = atoi( argv[ai+1] );
1788 ai++;
1789 break;
1790 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001791
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001792 ai++;
1793 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001794
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001795 fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
1796 fprintf( stderr, "<XAPP> starting %d threads\\n", nthreads );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001797
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001798 x = new Xapp( port, true );
E. Scott Danielsef362052020-07-22 15:49:54 -04001799 x->Add_msg_cb( 1, cb1, x ); // register callbacks
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001800 x->Add_msg_cb( 2, cb2, NULL );
1801 x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001802
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001803 x->Run( nthreads ); // let framework drive
1804 // control should not return
1805 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001806
E. Scott Danielsd486a172020-07-29 12:39:54 -04001807 Figure 30: Simple callback application.
E. Scott Daniels97204c82020-06-29 15:39:57 -04001808
1809
1810
1811Looping Sender
1812--------------
1813
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001814This is another very simple application which demonstrates
1815how an application can control its own listen loop while
1816sending messages. As with the other examples, some error
1817checking is skipped, and short cuts have been made in order
1818to keep the example small and to the point.
E. Scott Daniels97204c82020-06-29 15:39:57 -04001819
1820
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001821 ::
E. Scott Daniels97204c82020-06-29 15:39:57 -04001822
1823
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001824 #include <stdio.h>
1825 #include <string.h>
1826 #include <unistd.h>
E. Scott Daniels97204c82020-06-29 15:39:57 -04001827
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001828 #include <iostream>
1829 #include <memory>
E. Scott Daniels97204c82020-06-29 15:39:57 -04001830
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001831 #include "ricxfcpp/xapp.hpp"
E. Scott Daniels97204c82020-06-29 15:39:57 -04001832
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001833 extern int main( int argc, char** argv ) {
1834 std::unique_ptr<Xapp> xfw;
1835 std::unique_ptr<xapp::Message> msg;
1836 xapp::Msg_component payload; // special type of unique pointer to the payload
E. Scott Daniels97204c82020-06-29 15:39:57 -04001837
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001838 int sz;
1839 int len;
1840 int i;
1841 int ai;
1842 int response_to = 0; // max timeout wating for a response
1843 char* port = (char *) "4555";
1844 int mtype = 0;
1845 int rmtype; // received message type
1846 int delay = 1000000; // mu-sec delay; default 1s
E. Scott Daniels97204c82020-06-29 15:39:57 -04001847
1848
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001849 // very simple flag processing (no bounds/error checking)
1850 while( ai < argc ) {
1851 if( argv[ai][0] != '-' ) {
1852 break;
1853 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001854
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001855 // we only support -x so -xy must be -x -y
1856 switch( argv[ai][1] ) {
1857 // delay between messages (mu-sec)
1858 case 'd':
1859 delay = atoi( argv[ai+1] );
1860 ai++;
1861 break;
1862
1863 case 'p':
1864 port = argv[ai+1];
1865 ai++;
1866 break;
1867
1868 // timeout in seconds; we need to convert to ms for rmr calls
1869 case 't':
1870 response_to = atoi( argv[ai+1] ) * 1000;
1871 ai++;
1872 break;
1873 }
1874 ai++;
1875 }
1876
1877 fprintf( stderr, "<XAPP> response timeout set to: %d\\n", response_to );
1878 fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
1879
1880 // get an instance and wait for a route table to be loaded
1881 xfw = std::unique_ptr<Xapp>( new Xapp( port, true ) );
1882 msg = xfw->Alloc_msg( 2048 );
1883
1884 for( i = 0; i < 100; i++ ) {
1885 mtype++;
1886 if( mtype > 10 ) {
1887 mtype = 0;
1888 }
1889
1890 // we'll reuse a received message; get max size
1891 sz = msg->Get_available_size();
1892
1893 // direct access to payload; add something silly
1894 payload = msg->Get_payload();
1895 len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i );
1896
1897 // payload updated in place, prevent copy by passing nil
1898 if ( ! msg->Send_msg( mtype, xapp::Message::NO_SUBID, len, NULL )) {
1899 fprintf( stderr, "<SNDR> send failed: %d\\n", i );
1900 }
1901
1902 // receive anything that might come back
1903 msg = xfw->Receive( response_to );
1904 if( msg != NULL ) {
1905 rmtype = msg->Get_mtype();
1906 payload = msg->Get_payload();
1907 fprintf( stderr, "got: mtype=%d payload=(%s)\\n",
1908 rmtype, (char *) payload.get() );
1909 } else {
1910 msg = xfw->Alloc_msg( 2048 );
1911 }
1912
1913 if( delay > 0 ) {
1914 usleep( delay );
1915 }
1916 }
1917 }
1918
E. Scott Danielsd486a172020-07-29 12:39:54 -04001919 Figure 31: Simple looping sender application.
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001920
1921
1922
E. Scott Danielsd486a172020-07-29 12:39:54 -04001923Alarm Generation
1924----------------
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001925
E. Scott Danielsd486a172020-07-29 12:39:54 -04001926This is an extension of a previous example which sends an
1927alarm during initialisation and clears the alarm as soon as
1928messages are being received. It is unknown if this is the
1929type of alarm that is expected at the collector, but
1930illustrates how an alarm is allocated, raised and cleared.
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001931
1932
E. Scott Danielsd486a172020-07-29 12:39:54 -04001933 ::
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001934
1935
E. Scott Danielsd486a172020-07-29 12:39:54 -04001936 #include <stdio.h>
1937 #include <string.h>
1938 #include <unistd.h>
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001939
E. Scott Danielsd486a172020-07-29 12:39:54 -04001940 #include <iostream>
1941 #include <memory>
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001942
E. Scott Danielsd486a172020-07-29 12:39:54 -04001943 #include "ricxfcpp/xapp.hpp"
1944 #include "ricxfcpp/alarm.hpp"
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001945
E. Scott Danielsd486a172020-07-29 12:39:54 -04001946 extern int main( int argc, char** argv ) {
1947 std::unique_ptr<Xapp> xfw;
1948 std::unique_ptr<xapp::Message> msg;
1949 xapp::Msg_component payload; // special type of unique pointer to the payload
1950 std::unique_ptr<xapp::Alarm> alarm;
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001951
E. Scott Danielsd486a172020-07-29 12:39:54 -04001952 bool received = false; // false until we've received a message
1953 int sz;
1954 int len;
1955 int i;
1956 int ai = 1;
1957 int response_to = 0; // max timeout wating for a response
1958 char* port = (char *) "4555";
1959 int mtype = 0;
1960 int rmtype; // received message type
1961 int delay = 1000000; // mu-sec delay; default 1s
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001962
1963
E. Scott Danielsd486a172020-07-29 12:39:54 -04001964 // very simple flag processing (no bounds/error checking)
1965 while( ai < argc ) {
1966 if( argv[ai][0] != '-' ) {
1967 break;
1968 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001969
E. Scott Danielsd486a172020-07-29 12:39:54 -04001970 // we only support -x so -xy must be -x -y
1971 switch( argv[ai][1] ) {
1972 // delay between messages (mu-sec)
1973 case 'd':
1974 delay = atoi( argv[ai+1] );
1975 ai++;
1976 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001977
E. Scott Danielsd486a172020-07-29 12:39:54 -04001978 case 'p':
1979 port = argv[ai+1];
1980 ai++;
1981 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001982
E. Scott Danielsd486a172020-07-29 12:39:54 -04001983 // timeout in seconds; we need to convert to ms for rmr calls
1984 case 't':
1985 response_to = atoi( argv[ai+1] ) * 1000;
1986 ai++;
1987 break;
1988 }
1989 ai++;
1990 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001991
E. Scott Danielsd486a172020-07-29 12:39:54 -04001992 fprintf( stderr, "<XAPP> response timeout set to: %d\\n", response_to );
1993 fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001994
E. Scott Danielsd486a172020-07-29 12:39:54 -04001995 // get an instance and wait for a route table to be loaded
1996 xfw = std::unique_ptr<Xapp>( new Xapp( port, true ) );
1997 msg = xfw->Alloc_msg( 2048 );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001998
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001999
E. Scott Danielsd486a172020-07-29 12:39:54 -04002000 // raise an unavilable alarm which we'll clear on the first recevied message
2001 alarm = xfw->Alloc_alarm( "meid-1234" );
2002 alarm->Raise( xapp::Alarm::SEV_MINOR, 13, "unavailable", "no data recevied" );
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04002003
E. Scott Danielsd486a172020-07-29 12:39:54 -04002004 for( i = 0; i < 100; i++ ) {
2005 mtype++;
2006 if( mtype > 10 ) {
2007 mtype = 0;
2008 }
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04002009
E. Scott Danielsd486a172020-07-29 12:39:54 -04002010 // we'll reuse a received message; get max size
2011 sz = msg->Get_available_size();
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04002012
E. Scott Danielsd486a172020-07-29 12:39:54 -04002013 // direct access to payload; add something silly
2014 payload = msg->Get_payload();
2015 len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04002016
E. Scott Danielsd486a172020-07-29 12:39:54 -04002017 // payload updated in place, prevent copy by passing nil
2018 if ( ! msg->Send_msg( mtype, xapp::Message::NO_SUBID, len, NULL )) {
2019 fprintf( stderr, "<SNDR> send failed: %d\\n", i );
2020 }
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04002021
E. Scott Danielsd486a172020-07-29 12:39:54 -04002022 // receive anything that might come back
2023 msg = xfw->Receive( response_to );
2024 if( msg != NULL ) {
2025 if( ! received ) {
2026 // clear the alarm on first received message
2027 alarm->Clear( xapp::Alarm::SEV_MINOR, 13, "messages flowing", "" );
2028 received = true;
2029 }
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04002030
E. Scott Danielsd486a172020-07-29 12:39:54 -04002031 rmtype = msg->Get_mtype();
2032 payload = msg->Get_payload();
2033 fprintf( stderr, "got: mtype=%d payload=(%s)\\n",
2034 rmtype, (char *) payload.get() );
2035 } else {
2036 msg = xfw->Alloc_msg( 2048 );
2037 }
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04002038
E. Scott Danielsd486a172020-07-29 12:39:54 -04002039 if( delay > 0 ) {
2040 usleep( delay );
2041 }
2042 }
2043 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04002044
E. Scott Danielsd486a172020-07-29 12:39:54 -04002045 Figure 32: Simple looping sender application with alarm
2046 generation.
2047
2048
2049
2050Configuration Interface
2051-----------------------
2052
2053This example is a simple illustration of how the
2054configuration file support (xAPP descriptor) can be used to
2055suss out configuration parameters before creating the Xapp
2056object. The example also illustrates how a notification
2057callback can be used to react to changes in the
2058configuration.
2059
2060
2061 ::
2062
2063 #include <stdio.h>
2064
2065 #include "ricxfcpp/config.hpp"
2066 #include "ricxfcpp/message.hpp"
2067 #include "ricxfcpp/msg_component.hpp"
2068 #include <ricxfcpp/metrics.hpp>
2069 #include "ricxfcpp/xapp.hpp"
2070
2071 int vlevel = 0; // verbose mode set via config
2072
2073 /*
2074 Just print something to the tty when we receive a message
2075 and are in verbose mode.
2076 */
2077 void cb1( xapp::Message& mbuf, int mtype, int subid, int len,
2078 xapp::Msg_component payload, void* data ) {
2079 if( vlevel > 0 ) {
2080 fprintf( stdout, "message received is %d bytes long\\n", len );
2081 }
2082 }
2083
2084 /*
2085 Driven when the configuration changes. We snarf the verbose
2086 level from the new config and update it. If it changed to
2087 >0, incoming messages should be recorded with a tty message.
2088 If set to 0, then tty output will be disabled.
2089 */
2090 void config_cb( xapp::Config& c, void* data ) {
2091 int* vp;
2092
2093 if( (vp = (int *) data) != NULL ) {
2094 *vp = c.Get_control_value( "verbose_level", *vp );
2095 }
2096 }
2097
2098 int main( int argc, char** argv ) {
2099 Xapp* x;
2100 int nthreads = 1;
2101 std::unique_ptr<xapp::Config> cfg;
2102
2103 // only parameter recognised is the config file name
2104 if( argc > 1 ) {
2105 cfg = std::unique_ptr<xapp::Config>( new xapp::Config( std::string( argv[1] ) ) );
2106 } else {
2107 cfg = std::unique_ptr<xapp::Config>( new xapp::Config( ) );
2108 }
2109
2110 // must get a port from the config; no default
2111 auto port = cfg->Get_port( "rmr-data" );
2112 if( port.empty() ) {
2113 fprintf( stderr, "<FAIL> no port in config file\\n" );
2114 exit( 1 );
2115 }
2116
2117 // dig other data from the config
2118 vlevel = cfg->Get_control_value( "verbose_level", 0 );
2119 nthreads = cfg->Get_control_value( "thread_count", 1 );
2120 // very simple flag processing (no bounds/error checking)
2121
2122 if( vlevel > 0 ) {
2123 fprintf( stderr, "<XAPP> listening on port: %s\\n", port.c_str() );
2124 fprintf( stderr, "<XAPP> starting %d threads\\n", nthreads );
2125 }
2126
2127 // register the config change notification callback
2128 cfg->Set_callback( config_cb, (void *) &vlevel );
2129
2130 x = new Xapp( port.c_str(), true );
2131 x->Add_msg_cb( 1, cb1, x ); // register message callback
2132
2133 x->Run( nthreads ); // let framework drive
2134 // control should not return
2135 }
2136
2137 Figure 33: Simple application making use of the
2138 configuration object.
E. Scott Daniels97204c82020-06-29 15:39:57 -04002139