Add support for config file parsing and watching
[ric-plt/xapp-frame-cpp.git] / docs / user-guide.rst
1 ============
2 USER'S GUIDE
3 ============
4 .. 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
12
13
14 INTRODUCTION
15 ============
16
17 The C++ framework allows the programmer to create an instance
18 of the ``Xapp`` object which then can be used as a foundation
19 for the application. The ``Xapp`` object provides a message
20 level interface to the RIC Message Router (RMR), including
21 the ability to register callback functions which the instance
22 will drive as messages are received; much in the same way
23 that an X-windows application is driven by the window manager
24 for all activity. The xApp may also choose to use its own
25 send/receive loop, and thus is not required to use the
26 callback driver mechanism provided by the framework.
27
28
29 Termonology
30 -----------
31
32 To avoid confusion the term **xAPP** is used in this document
33 to refer to the user's application code which is creating
34 ``Xapp,`` and related objects provided by the *framework.*
35 The use of *framework* should be taken to mean any of the
36 classes and/or support functions which are provided by the
37 ``ricxfcpp`` library.
38
39
40 THE FRAMEWORK API
41 =================
42
43 The C++ framework API consists of the creation of the xApp
44 object, and invoking desired functions via the instance of
45 the object. The following paragraphs cover the various steps
46 involved to create an xApp instance, wait for a route table
47 to arrive, send a message, and wait for messages to arrive.
48
49
50 The Namespace
51 -------------
52
53 Starting 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
63 This is a breaking change and as such the major version was
64 bumpped from 1 to 2.
65
66
67 Creating the xApp instance
68 --------------------------
69
70 The creation of the xApp instance is as simple as invoking
71 the object's constructor with two required parameters:
72
73
74        .. list-table::
75          :widths: auto
76          :header-rows: 0
77          :class: borderless
78
79
80          * - **port**
81
82            -
83
84              A C string (pointer to char) which defines the port that
85
86              RMR will open to listen for connections.
87
88
89
90
91
92              |
93
94
95
96          * - **wait**
97
98            -
99
100              A Boolean value which indicates whether or not the
101
102              initialization process should wait for the arrival of a
103
104              valid route table before completing. When true is
105
106              supplied, the initialization will not complete until RMR
107
108              has received a valid route table (or one is located via
109
110              the ``RMR_SEED_RT`` environment variable).
111
112
113
114 The following code sample illustrates the simplicity of
115 creating the instance of the xApp object.
116
117
118 ::
119
120       #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
126
127           xapp = std::unique_ptr<Xapp>(
128                 new Xapp( listen_port, wait4table ) );
129       }
130
131 Figure 1: Creating an xAPP instance.
132
133 From a compilation perspective, the following is the simple
134 compiler invocation string needed to compile and link the
135 above program (assuming that the sample code exists in a file
136 called ``man_ex1.cpp``.
137
138
139 ::
140
141      g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread
142
143
144 The above program, while complete and capable of being
145 compiled, does nothing useful. When invoked, RMR will be
146 initialized and will begin listening for a route table;
147 blocking the return to the main program until one is
148 received. When a valid route table arrives, initialization
149 will complete and the program will exit as there is no code
150 following the instruction to create the object.
151
152
153 LISTENING FOR MESSAGES
154 ======================
155
156 The program in the previous example can be extended with just
157 a few lines of code to enable it to receive and process
158 messages. The application needs to register a callback
159 function for each message type which it desires to process.
160
161 Once registered, each time a message is received the
162 registered callback for the message type will be invoked by
163 the framework.
164
165
166 Callback Signature
167 ------------------
168
169 As with most callback related systems, a callback must have a
170 well known function signature which generally passes event
171 related information and a "user" data pointer which was
172 registered with the function. The following is the prototype
173 which callback functions must be defined with:
174
175
176 ::
177
178       void cb_name( xapp::Message& m, int mtype, int subid,
179             int payload_len, xapp::Msg_component payload,
180             void* usr_data );
181
182 Figure 2: Callback function signature
183
184 The parameters passed to the callback function are as
185 follows:
186
187
188        .. list-table::
189          :widths: auto
190          :header-rows: 0
191          :class: borderless
192
193
194          * - **m**
195
196            -
197
198              A reference to the Message that was received.
199
200
201
202
203
204              |
205
206
207
208          * - **mtype**
209
210            -
211
212              The message type (allows for disambiguation if the
213
214              callback is registered for multiple message types).
215
216
217
218
219
220              |
221
222
223
224          * - **subid**
225
226            -
227
228              The subscription ID from the message.
229
230
231
232
233
234              |
235
236
237
238          * - **payload len**
239
240            -
241
242              The number of bytes which the sender has placed into the
243
244              payload.
245
246
247
248
249
250              |
251
252
253
254          * - **payload**
255
256            -
257
258              A direct reference (smart pointer) to the payload. (The
259
260              smart pointer is wrapped in a special class in order to
261
262              provide a custom destruction function without burdening
263
264              the xApp developer with that knowledge.)
265
266
267
268
269
270              |
271
272
273
274          * - **user data**
275
276            -
277
278              A pointer to user data. This is the pointer that was
279
280              provided when the function was registered.
281
282
283
284 To illustrate the use of a callback function, the previous
285 code example has been extended to add the function, register
286 it for message types 1000 and 1001, and to invoke the
287 ``Run()`` function in the framework (explained in the next
288 section).
289
290 ::
291
292       #include <memory>
293       #include <ricxfcpp/xapp.hpp>
294       long m1000_count = 0;    // message counters, one for each type
295       long m1001_count = 0;
296
297       // 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;
301
302           if( (counter = (long *) data) != NULL ) {
303               (*counter)++;
304           }
305       }
306
307       int main( ) {
308           std::unique_ptr<Xapp> xapp;
309           char* listen_port = (char *) "4560";
310           bool  wait4table = false;
311
312           xapp = std::unique_ptr<Xapp>(
313                 new Xapp( listen_port, wait4table ) );
314
315           // 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 );
318
319           xapp->Run( 1 );        // start the callback driver
320       }
321
322 Figure 3: Callback function example.
323
324 As before, the program does nothing useful, but now it will
325 execute and receive messages. For this example, the same
326 function can be used to increment the appropriate counter
327 simply by providing a pointer to the counter as the user data
328 when the callback function is registered. In addition, a
329 subtle change from the previous example has been to set the
330 wait for table flag to ``false.``
331
332 For an xApp that is a receive only application (never sends)
333 it is not necessary to wait for RMR to receive a table from
334 the Route Manager.
335
336
337 Registering A Default Callback
338 ------------------------------
339
340 The xApp may also register a default callback function such
341 that the function will be invoked for any message that does
342 not have a registered callback. If the xAPP does not register
343 a default callback, any message which cannot be mapped to a
344 known callback function is silently dropped. A default
345 callback is registered by providing a *generic* message type
346 of ``xapp->DEFAULT_CALLBACK`` on an ``Add_msg_cb`` call.
347
348
349 The Framework Callback Driver
350 -----------------------------
351
352 The ``Run()`` function within the Xapp object is invoked to
353 start the callback driver, and the xApp should not expect the
354 function to return under most circumstances. The only
355 parameter that the ``Run()`` function expects is the number
356 of threads to start. For each thread requested, the framework
357 will start a listener thread which will allow received
358 messages to be processed in parallel. If supplying a value
359 greater than one, the xApp must ensure that the callback
360 functions are thread safe as it is very likely that the same
361 callback function will be invoked concurrently from multiple
362 threads.
363
364
365 SENDING MESSAGES
366 ================
367
368 It is very likely that most xApps will need to send messages
369 and will not operate in "receive only" mode. Sending the
370 message is a function of the message object itself and can
371 take one of two forms:
372
373
374    * Replying to the sender of a received message
375
376    * Sending a message (routed based on the message type and
377      subscription ID)
378
379
380 When replying to the sender, the message type and
381 subscription ID are not used to determine the destination of
382 the message; RMR ensures that the message is sent back to the
383 originating xApp. The xApp may still need to change the
384 message type and/or the subscription ID in the message prior
385 to using the reply function.
386
387 To provide for both situations, two reply functions are
388 supported by the Message object as illustrated with the
389 following prototypes.
390
391
392 ::
393
394      bool Send_response(  int mtype, int subid, int response_len,
395           std:shared_ptr<unsigned char> response );
396
397      bool Send_response(  int response_len, std::shared_ptr<unsigned char> response );
398
399 Figure 4: Reply function prototypes.
400
401 In the first prototype the xApp must supply the new message
402 type and subscription ID values, where the second function
403 uses the values which are currently set in the message.
404 Further, the new payload contents, and length, are supplied
405 to both functions; the framework ensures that the message is
406 large enough to accommodate the payload, reallocating it if
407 necessary, and copies the response into the message payload
408 prior to sending. Should the xApp need to change either the
409 message type, or the subscription ID, but not both, the
410 ``NO_CHANGE`` constant can be used as illustrated below.
411
412
413 ::
414
415       msg->Send_response( xapp::Message::NO_CHANGE, xapp::Message::NO_SUBID,
416           pl_length, (unsigned char *) payload );
417
418 Figure 5: Send response prototype.
419
420 In addition to the two function prototypes for
421 ``Send_response()`` there are two additional prototypes which
422 allow the new payload to be supplied as a shared smart
423 pointer. The other parameters to these functions are
424 identical to those illustrated above, and thus are not
425 presented here.
426
427 The ``Send_msg()`` set of functions supported by the Message
428 object are identical to the ``Send_response()`` functions and
429 are shown below.
430
431
432 ::
433
434       bool Send_msg( int mtype, int subid, int payload_len,
435           std::shared_ptr<unsigned char> payload );
436
437       bool Send_msg( int mtype, int subid, int payload_len,
438           unsigned char* payload );
439
440       bool Send_msg( int payload_len,
441           std::shared_ptr<unsigned char> payload );
442
443       bool Send_msg( int payload_len, unsigned char* payload );
444
445 Figure 6: Send function prototypes.
446
447 Each send function accepts the message, copies in the payload
448 provided, sets the message type and subscription ID (if
449 provided), and then causes the message to be sent. The only
450 difference between the ``Send_msg()`` and
451 ``Send_response()`` functions is that the destination of the
452 message is selected based on the mapping of the message type
453 and subscription ID using the current routing table known to
454 RMR.
455
456
457 Direct Payload Manipulation
458 ---------------------------
459
460 For some applications, it might be more efficient to
461 manipulate the payload portion of an Xapp Message in place,
462 rather than creating it and relying on a buffer copy when the
463 message is finally sent. To achieve this, the xApp must
464 either use the smart pointer to the payload passed to the
465 callback function, or retrieve one from the message using
466 ``Get_payload()`` when working with a message outside of a
467 callback function. Once the smart pointer is obtained, the
468 pointer's get() function can be used to directly reference
469 the payload (unsigned char) bytes.
470
471 When working directly with the payload, the xApp must take
472 care not to write more than the actual payload size which can
473 be extracted from the Message object using the
474 ``Get_available_size()`` function.
475
476 When sending a message where the payload has been directly
477 altered, and no extra buffer copy is needed, a NULL pointer
478 should be passed to the Message send function. The following
479 illustrates how the payload can be directly manipulated and
480 returned to the sender (for simplicity, there is no error
481 handling if the payload size of the received message isn't
482 large enough for the response string, the response is just
483 not sent).
484
485
486 ::
487
488       Msg_component payload;  // smart reference
489       int pl_size;            // max size of payload
490
491       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       }
497
498 Figure 7: Send message without buffer copy.
499
500
501
502 Sending Multiple Responses
503 --------------------------
504
505 It is likely that the xApp will wish to send multiple
506 responses back to the process that sent a message that
507 triggered the callback. The callback function may invoke the
508 ``Send_response()`` function multiple times before returning.
509
510 After each call, the Message retains the necessary
511 information to allow for a subsequent invocation to send more
512 data. It should be noted though, that after the first call to
513 ``{Send_response()`` the original payload will be lost; if
514 necessary, the xApp must make a copy of the payload before
515 the first response call is made.
516
517
518 Message Allocation
519 ------------------
520
521 Not all xApps will be "responders," meaning that some xApps
522 will need to send one or more messages before they can expect
523 to receive any messages back. To accomplish this, the xApp
524 must first allocate a message buffer, optionally initialising
525 the payload, and then using the message's ``Send_msg()``
526 function to send a message out. The framework's
527 ``Alloc_msg()`` function can be used to create a Message
528 object with a desired payload size.
529
530
531 FRAMEWORK PROVIDED CALLBACKS
532 ============================
533
534 The framework itself may provide message handling via the
535 driver such that the xApp might not need to implement some
536 message processing functionality. Initially, the C++
537 framework will provide a default callback function to handle
538 the RMR based health check messages. This callback function
539 will assume that if the message was received, and the
540 callback invoked, that all is well and will reply with an OK
541 state. If the xApp should need to override this simplistic
542 response, all it needs to do is to register its own callback
543 function for the health check message type.
544
545
546 JSON SUPPORT
547 ============
548
549 The C++ xAPP framework provides a very lightweight json
550 parser and data hash facility. Briefly, a json hash (Jhash)
551 can be established by creating an instance of the Jhash
552 object with a string of valid json. The resulting object's
553 functions can then be used to read values from the resulting
554 hash.
555
556
557 Creating The Jhash Object
558 -------------------------
559
560 The Jhash object is created simply by passing a json string
561 to the constructor.
562
563 ::
564
565       #include <ricxfcpp/Jhash.hpp>
566
567       std::string jstring = "{ \\"tag\\": \\"Hello World\\" }";
568       Jhash*  jh;
569
570       jh =  new Jhash( jstring.c_str() );
571
572 Figure 8: The creation of the Jhash object.
573
574 Once the Jhash object has been created any of the methods
575 described in the following paragraphs can be used to retrieve
576 the data:
577
578
579 Json Blobs
580 ----------
581
582 Json objects can be nested, and the nesting is supported by
583 this representation. The approach taken by Jhash is a
584 "directory view" approach, where the "current directory," or
585 current *blob,* limits the scope of visible fields.
586
587 As an example, the json contained in figure 9, contains a
588 "root" blob and two *sub-blobs* (address and lease_info).
589
590
591 ::
592
593       {
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       }
611
612 Figure 9: Sample json with a root and two blobs.
613
614 Upon creation of the Jhash object, the *root* fields,
615 ``lodge_name,`` ``member_count,`` and ``grand_poobah`` are
616 immediately available. The fields in the *sub-blobs* are
617 available only when the correct blob is selected. The code
618 sample in figure 10 illustrates how a *sub-blob* is selected.
619
620 ::
621
622       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
625
626 Figure 10: Blob selection example.
627
628 Currently, the selected blob must be unset in order to select
629 a blob at the root level; unset always sets the root blob.
630 Attempting to use the ``Set_blob`` function will attempt to
631 select the named blob from the current blob, and not the
632 root.
633
634
635 Simple Value Extraction
636 -----------------------
637
638 Simple values are the expected data types *string, value,*
639 and *boolean.* This lightweight json parser treats all values
640 as floating point numbers and does not attempt to maintain a
641 separate integer type. A fourth type, *null,* is supported to
642 allow the user to expressly check for a field which is
643 defined but has no value; as opposed to a field that was
644 completely missing from the data. The following are the
645 prototypes for the functions which allow values to be
646 extracted:
647
648
649 ::
650
651       std::string String( const char* name );
652       float Value( const char* name );
653       bool Bool( const char* name );
654
655
656 Each of these functions returns the value associated with the
657 field with the given *name.* If the value is missing, the
658 following default values are returned:
659
660
661        .. list-table::
662          :widths: 15,80
663          :header-rows: 0
664          :class: borderless
665
666
667          * - **String**
668
669            -
670
671              An empty string (.e.g "").
672
673
674
675              |
676
677
678
679          * - **Value**
680
681            -
682
683              Zero (e.g 0.0)
684
685
686
687              |
688
689
690
691          * - **bool**
692
693            -
694
695              false
696
697
698
699 If the user needs to disambiguate between a missing value and
700 the default value either the ``Missing`` or ``Exists``
701 function should be used first.
702
703
704 Testing For Existing and Missing Fields
705 ---------------------------------------
706
707 Two functions allow the developer to determine whether or not
708 a field is included in the json. Both of these functions work
709 on the current *blob,* therefore it is important to ensure
710 that the correct blob is selected before using either of
711 these functions. The prototypes for the ``Exists`` and
712 ``Missing`` functions are below:
713
714 ::
715
716       bool Exists( const char* name );
717       bool Is_missing( const char* name );
718
719 The ``Exists`` function returns *true* if the field name
720 exists in the json and *false* otherwise. Conversely, the
721 ``Missing`` function returns *true* when the field name does
722 not exist in the json.
723
724
725 Testing Field Type
726 ------------------
727
728 The ``Exists`` and ``Missing`` functions might not be enough
729 for the user code to validate the data that it has. To assist
730 with this, several functions allow direct type testing on a
731 field in the current blob. The following are the prototypes
732 for these functions:
733
734 ::
735
736       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 );
740
741
742 Each of these functions return *true* if the field with the
743 given name is of the type being tested for.
744
745
746 Arrays
747 ------
748
749 Arrays are supported in the same manner as simple field
750 values with the addition of the need to supply an array index
751 when fetching values from the object. In addition, there is a
752 *length* function which can be used to determine the number
753 of elements in the named array. The prototypes for the array
754 based functions are below:
755
756 ::
757
758       int Array_len( const char* name );
759
760       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 );
764
765       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 );
768
769
770 For each of these functions the ``eidx`` is the zero based
771 element index which is to be tested or selected.
772
773
774 Arrays of Blobs
775 ---------------
776
777 An array containing blobs, rather than simple field value
778 pairs, the blob must be selected prior to using it, just as a
779 sub-blob needed to be selected. The ``Set_blob_ele`` function
780 is used to do this and has the following prototype:
781
782 ::
783
784       bool Set_blob_ele( const char* name, int eidx );
785
786
787 As with selecting a sub-blob, an unset must be performed
788 before selecting the next blob. Figure 11 illustrates how
789 these functions can be used to read and print values from the
790 json in figure 12.
791
792 ::
793
794       "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       ]
800
801 Figure 11: Json array containing blobs.
802
803
804 ::
805
806       std::string mname;
807       float mnum;
808       int len;
809
810       len = jh->Array_len( (char *) "members" );
811       for( i = 0; i < len; i++ ) {
812           jh->Set_blob_ele( (char *) "members", i );  // select blob
813
814           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 );
817
818           jh->Unset_blob();                           // back to root
819       }
820
821 Figure 12: Code to process the array of blobs.
822
823
824
825 ALARM MANAGER INTERFACE
826 =======================
827
828 The C++ framework provides an API which allows the xAPP to
829 easily construct and generate alarm messages. Alarm messages
830 are a special class of RMR message, allocated in a similar
831 fashion as an RMR message through the framework's
832 ``Alloc_alarm()`` function.
833
834 The API consists of the following function types:
835
836
837        .. list-table::
838          :widths: auto
839          :header-rows: 0
840          :class: borderless
841
842
843          * - **Raise**
844
845            -
846
847              Cause the alarm to be assigned a severity and and sent via
848
849              RMR message to the alarm collector process.
850
851
852
853
854
855              |
856
857
858
859          * - **Clear**
860
861            -
862
863              Cause a clear message to be sent to the alarm collector.
864
865
866
867
868
869              |
870
871
872
873          * - **Raise Again**
874
875            -
876
877              Cause a clear followed by a raise message to be sent to
878
879              the alarm collector.
880
881
882
883
884
885 Allocating Alarms
886 -----------------
887
888 The ``xapp`` function provided by the framework is used to
889 create an alarm object. Once the xAPP has an alarm object it
890 can be used to send one, or more, alarm messages to the
891 collector.
892
893 The allocation function has three prototypes which allow the
894 xAPP to create an alarm with an initial set of information as
895 is appropriate. The following are the prototypes for the
896 allocate 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
905 Figure 13: Alarm allocation prototypes.
906
907 Each of the allocation functions returns a unique pointer to
908 the alarm. In the simplest form (1) the alarm is initialised
909 with an empty meid (managed element ID) string, and unset
910 problem ID (-1). The second prototype allows the xAPP to
911 supply the meid, and in the third form both the problem ID
912 and the meid are used to initialise the alarm.
913
914
915 Raising An Alarm
916 ----------------
917
918 Once an alarm has been allocated, its ``Raise()`` function
919 can be used to cause the alarm to be sent to the collector.
920 The raise process allows the xAPP to perform the following
921 modifications 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
933 The following are the prototypes for the ``Raise()``
934 functions of an alarm object: ..... In its simplest form (1)
935 the ``Raise()`` function will send the alarm without making
936 any changes to the data. The final two forms allow the xAPP
937 to supply additional data which is added to the alarm before
938 sending the message. Each of the raise functions returns
939 ``true`` on success and ``false`` if the alarm message could
940 not be sent.
941
942
943 Severity
944 --------
945
946 The severity is one of the ``SEV_`` constants listed below.
947 These map to alarm collector strings and insulate the xAPP
948 from any future alarm collector changes. The specific meaning
949 of these severity types are defined by the alarm collector
950 and thus no attempt is made to guess what their actual
951 meaning 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
962 Figure 14: Severity constants available in alarm.hpp.
963
964
965 The Problem ID
966 --------------
967
968 The problem ID is an integer which is assigned by the xAPP.
969 The framework makes no attempt to verify that it has been se,
970 nor does it attempt to validate the value. If the xAPP does
971 not set the value, ``-1`` is used.
972
973
974 Information Strings
975 -------------------
976
977 The two information strings are also xAPP defined and provide
978 the information that the xAPP deems necessary and related to
979 the alarm. What the collector expects, and how these strings
980 are used, is beyond the scope of the framework to describe or
981 validate. If not supplied, empty strings are sent in the
982 alarm message.
983
984
985 Clearing An Alarm
986 -----------------
987
988 The ``Clear()`` function of an alarm may be used to send a
989 clear message. In a manner similar to the ``Raise()``
990 functions, the ``Clear()`` functions allow the existing alarm
991 data to be sent without change, or for the xAPP to modify the
992 data before the message is sent to the collector. The
993 following 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
1003 Figure 15: Clear function prototypes.
1004
1005 Each of the clear functions returns ``true`` on success and
1006 ``false`` if the alarm message could not be sent.
1007
1008 The ``Clear_all()`` function sends a special action code to
1009 the collector which is assumed to clear all alarms. However,
1010 it is unknown whether that implies **all** alarms, or all
1011 alarms matching the ``problem_id,`` or some other
1012 interpretation. Please consult the alarm collector
1013 documentation for these specifics.
1014
1015
1016 Adjusting Alarm Contents
1017 ------------------------
1018
1019 It might be necessary for the xAPP to adjust the alarm
1020 contents outside of the scope of the ``Raise()`` function, or
1021 to adjust data that cannot be manipulated by ``Raise().`` The
1022 following 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
1035 Figure 16: Alarm Setters
1036
1037
1038
1039 METRICS SUPPORT
1040 ===============
1041
1042 The C++ xAPP framework provides a lightweight interface to
1043 the metrics gateway allowing the xAPP to create and send
1044 metrics updates without needing to understand the underlying
1045 message format. From the xAPP's perspective, the metrics
1046 object is created with one or more key/value measurement
1047 pairs and then is sent to the process responsible for
1048 forwarding them to the various collection processes. The
1049 following sections describe the Metrics object and the API
1050 associated with it.
1051
1052
1053 Creating The Metrics Object
1054 ---------------------------
1055
1056 The ``xapp`` object can be created directly, or via the xapp
1057 framework. When creating directly the xAPP must supply an RMR
1058 message for the object to use; when the framework is used to
1059 create the object, the message is created as as part of the
1060 process. The framework provides three constructors for the
1061 metrics instance allowing the xAPP to supply the measurement
1062 source, the source and reporter, or to default to using the
1063 xAPP name as both the source and reporter (see section
1064 *Source and Reporter* for a more detailed description of
1065 these). The framework constructors are illustrated in figure
1066 17.
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
1075 Figure 17: The framework constructors for creating an
1076 instance 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
1089 Figure 18: Metrics instance creation using the framework.
1090
1091 Figures 18 illustrates how the framework constructor can be
1092 used to create a metrics instance. While it is unlikely that
1093 an xAPP will create a metrics instance directly, there are
1094 three similar constructors available. These are prototypes
1095 are shown in figure 19 and their use is illustrated in figure
1096 20.
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
1104 Figure 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
1117 Figure 20: Direct creation of a metrics instance.
1118
1119
1120
1121 Adding Values
1122 -------------
1123
1124 Once an instance of the metrics object is created, the xAPP
1125 may push values in preparation to sending the measurement(s)
1126 to the collector. The ``Push_data()`` function is used to
1127 push 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
1135 Figure 21: Pushing key/value pairs into a metrics instance.
1136
1137
1138
1139 Sending A Measurement Set
1140 -------------------------
1141
1142 After all of the measurement key/value pairs have been added
1143 to the instance, the ``Send()`` function can be invoked to
1144 create the necessary RMR message and send that to the
1145 collection application. Following the send, the key/value
1146 pairs are cleared from the instance and the xAPP is free to
1147 start pushing values into the instance again. The send
1148 function has the following prototype and returns ``true`` on
1149 success and ``false`` if the measurements could not be sent.
1150
1151
1152 Source and Reporter
1153 -------------------
1154
1155 The alarm collector has the understanding that a measurement
1156 might be *sourced* from one piece of equipment, or software
1157 component, but reported by another. For auditing purposes it
1158 makes sense to distinguish these, and as such the metrics
1159 object allows the xAPP to identify the case when the source
1160 and reporter are something other than the xAPP which is
1161 generating the metrics message(s).
1162
1163 The *source* is the component to which the measurement
1164 applies. This could be a network interface card counting
1165 packets, a temperature sensor, or the xAPP itself reporting
1166 xAPP related metrics. The *reporter* is the application that
1167 is reporting the measurement(s) to the collector.
1168
1169 By default, both reporter and source are assumed to be the
1170 xAPP, and the name is automatically determined using the
1171 run-time supplied programme name. Should the xAPP need to
1172 report measurements for more than one source it has the
1173 option to create an instance for every reporter source
1174 combination, or to set the reporter and/or source with the
1175 generation of each measurement set. To facilitate the ability
1176 to change the source and/or the reporter without the need to
1177 create a new metrics instance, two *setter* functions are
1178 provided. 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
1186 Figure 22: Setter functions allowing the reporter and/or
1187 source to be set after construction.
1188
1189
1190
1191 CONFIGURATION SUPPORT
1192 =====================
1193
1194 The C++ xAPP framework provides the xAPP with an interface to
1195 load, parse and receive update notifications on the
1196 configuration. The configuration, also known as the xAPP
1197 descriptor, is assumed to be a file containing json with a
1198 well known structure, with some fields or *objects* used by
1199 an xAPP for configuration purposes. The following paragraphs
1200 describe the support that the framework provides to the xAPP
1201 with respect to the configuration aspects of the descriptor.
1202
1203
1204 The Config Object
1205 -----------------
1206
1207 The xAPP must create an instance of the ``config`` object in
1208 order to take advantage of the support. This is accomplished
1209 by using one of two constructors illustrated with code
1210 samples 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
1220 Figure 23: Creating a configuration instance.
1221
1222 The creation of the object causes the file to be found,
1223 loaded, after which the xAPP can use the instance functions
1224 to access the information it needs.
1225
1226
1227 Available Functions
1228 -------------------
1229
1230 Once a configuration has been created the following
1231 capabilities 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
1245 Control Values
1246 --------------
1247
1248 The ``controls`` section of the xAPP descriptor is generally
1249 used to supply a *flat* namespace of key/value pairs which
1250 the xAPP can use for configuration. These pairs are supplied
1251 by the xAPP author as a part of development, and thus are
1252 specific to each xAPP. The framework provides a general set
1253 of functions which allows a key to be searched for in this
1254 section and returned to the caller. Data is assumed to be one
1255 of three types: numeric (double), string, or boolean.
1256
1257 Two methods for each return type are supported with the more
1258 specific form allowing the xAPP to supply a default value to
1259 be used should the file not contain the requested field. The
1260 function 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
1273 Figure 24: The various controls section get functions.
1274
1275 If the more generic form of these functions is used, without
1276 a default value, the return values are false, "", and 0.0 in
1277 the respective order of the prototypes in figure 24.
1278
1279
1280 The RMR Port
1281 ------------
1282
1283 The ``messaging`` section of the xAPP descriptor provides the
1284 ability to define one or more RMR *listen ports* that apply
1285 to the xAPP(s) started in a given container. The xAPP may
1286 read a port value (as a string) using the defined port name
1287 via the ``Get_port`` function whose prototype is illustrated
1288 in figure 25 below.
1289
1290
1291 ::
1292
1293     std::string Get_port( std::string name );
1294
1295 Figure 25: The get port prototype.
1296
1297
1298
1299 Raw File Contents
1300 -----------------
1301
1302 While it is not anticipated to be necessary, the xAPP might
1303 need direct access to the raw contents of the configuration
1304 file. As a convenience the framework provides the
1305 ``Get_contents()`` function which reads the entire file into
1306 a standard library string and returns that to the calling
1307 function. Parsing and interpreting the raw contents is then
1308 up to the xAPP.
1309
1310
1311 Notification Of Changes
1312 -----------------------
1313
1314 When desired, the xAPP may register a notification callback
1315 function with the framework. This callback will be driven any
1316 time a change to the descriptor is detected. When a change is
1317 detected, the revised descriptor is read into the existing
1318 object (overlaying any previous information), before invoking
1319 the callback. The callback may then retrieve the updated
1320 values and make any adjustments which are necessary. The
1321 prototype for the xAPP callback function is described in
1322 figure 26.
1323
1324
1325 ::
1326
1327      void cb_name( xapp::Config& c, void* data )
1328
1329 Figure 26: The prototype which the xAPP configuration notify
1330 callback must use.
1331
1332
1333
1334 Enabling The Notifications
1335 --------------------------
1336
1337 Notifications are enabled by invoking the
1338 ``Set_callback()`` function. Once enabled, the framework will
1339 monitor the configuration source and invoke the callback upon
1340 change. This occurs in a separate thread than the main xAPP
1341 thread; it is up to the xAPP to guard against any potential
1342 data collisions when evaluating configuration changes. If the
1343 xAPP does not register a notification function the framework
1344 will not monitor the configuration for changes and the object
1345 will have static data. Figure 27 illustrates how the xAPP can
1346 define 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
1360 Figure 27: Small notification callback function allowing on
1361 the fly verbose level change.
1362
1363
1364 The xAPP would register the ``config_chg()`` function as the
1365 notification callback using the call illustrated in figure
1366 28.
1367
1368 ::
1369
1370
1371      auto conf = new xapp::Config();
1372      conf->Set_callback( config_chg );
1373
1374 Figure 28: Setting the notification callback and and
1375 activating notifications.
1376
1377
1378
1379
1380 xAPP Descriptor Notes
1381 ---------------------
1382
1383 While it is beyond the scope of this document to describe the
1384 complete contents of an xAPP descriptor file, it is prudent
1385 to mention several items which are related to the information
1386 used from the descriptor file. The following paragraphs
1387 discuss things which the xAPP developer should be aware of,
1388 and keep in mind when using the configuration class.
1389
1390
1391 The RMR Section
1392 ---------------
1393
1394 There is a deprecated section within the xAPP descriptor
1395 which has the title *rmr.* The *messaging* section provides
1396 more flexibility, and additional information and has been a
1397 replacement for the *rmr* section for all applications. The
1398 information in the *rmr* section should be kept consistent
1399 with the duplicated information in the *messaging* section as
1400 long as there are container management and/or platform
1401 applications (e.g. Route Manager) which are back level and do
1402 not recognise the *messaging* section. The configuration
1403 parsing and support provided by the framework will ignore the
1404 *rmr* section.
1405
1406
1407 EXAMPLE PROGRAMMES
1408 ==================
1409
1410 The following sections contain several example programmes
1411 which are written on top of the C++ framework. All of these
1412 examples are available in the code repository RIC xAPP C++
1413 framework 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
1418
1419
1420 RMR Dump xAPP
1421 -------------
1422
1423 The RMR dump application is an example built on top of the
1424 C++ xApp framework to both illustrate the use of the
1425 framework, and to provide a useful diagnostic tool when
1426 testing and troubleshooting xApps.
1427
1428 The RMR dump xApp isn't a traditional xApp inasmuch as its
1429 goal is to listen for message types and to dump information
1430 about the messages received to the TTY much as
1431 ``tcpdump`` does for raw packet traffic. The full source
1432 code, and Makefile, are in the ``examples`` directory of the
1433 C++ framework repo.
1434
1435 When invoked, the RMR dump program is given one or more
1436 message types to listen for. A callback function is
1437 registered for each, and the framework ``Run()`` function is
1438 invoked to drive the process. For each recognised message,
1439 and depending on the verbosity level supplied at program
1440 start, information about the received message(s) is written
1441 to the TTY. If the forwarding option, -f, is given on the
1442 command line, and an appropriate route table is provided,
1443 each received message is forwarded without change. This
1444 allows for the insertion of the RMR dump program into a flow,
1445 however if the ultimate receiver of a message needs to reply
1446 to that message, the reply will not reach the original
1447 sender, so RMR dump is not a complete "middle box"
1448 application.
1449
1450 The following is the code for this xAPP. Several functions,
1451 which provide logic unrelated to the framework, have been
1452 omitted. The full code is in the framework repository.
1453
1454
1455
1456    ::
1457
1458      #include <stdio.h>
1459      #include <unistd.h>
1460      #include <atomic>
1461
1462      #include "ricxfcpp/xapp.hpp"
1463
1464      /*
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;
1477
1478      // ----------------------------------------------------------------------
1479
1480      /*
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];
1487
1488          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] );
1493
1494              if( j == 16 ) {
1495                  cheater[j] = 0;
1496                  fprintf( stdout, " | %s\\n<RD> %04x | ", cheater, i+1 );
1497                  j = 0;
1498              }
1499          }
1500
1501          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      }
1510
1511      /*
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
1517
1518          curv = cbi.hdr--;
1519
1520          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              }
1527
1528              cbi.hdr = cbi.stats_freq;        // reset must be last
1529          }
1530      }
1531
1532      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;
1536
1537          if( (cbi = (cb_info_t *) data) == NULL ) {
1538              return;
1539          }
1540
1541          cbi->pcount++;
1542          stats( *cbi );            // gen stats & header if needed
1543
1544          if( cbi->vlevel > 0 ) {
1545              fprintf( stdout, "<RD> %-5d %-5d %02d %-5d \\n",
1546                      mtype, subid, mbuf.Get_state(), len );
1547
1548              if( cbi->vlevel > 1 ) {
1549                  dump(  payload.get(), len > 64 ? 64 : len );
1550              }
1551          }
1552
1553          if( cbi->forward ) {
1554              // forward with no change to len or payload
1555              mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL );
1556          }
1557      }
1558
1559      /*
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;
1566
1567          if( (cbi = (cb_info_t *) data) == NULL ) {
1568              return;
1569          }
1570
1571          cbi->icount++;
1572          stats( *cbi );
1573
1574          if( cbi->forward ) {
1575              // forward with no change to len or payload
1576              mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL );
1577          }
1578      }
1579
1580      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;
1588
1589          cbi = (cb_info_t *) malloc( sizeof( *cbi ) );
1590          cbi->pcount = 0;
1591          cbi->icount = 0;
1592          cbi->stats_freq = 10;
1593
1594          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              }
1600
1601              // 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;
1606
1607                  case 'p':                    // define port
1608                      port = argv[ai+1];
1609                      ai++;
1610                      break;
1611
1612                  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;
1619
1620                  case 't':                        // thread count
1621                      nthreads = atoi( argv[ai+1] );
1622                      if( nthreads < 1 ) {
1623                          nthreads = 1;
1624                      }
1625                      ai++;
1626                      break;
1627
1628                  case 'v':            // simple verbose bump
1629                      cbi->vlevel++;
1630                      break;
1631
1632                  case 'V':            // explicit verbose level
1633                      cbi->vlevel = atoi( argv[ai+1] );
1634                      ai++;
1635                      break;
1636
1637                  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              }
1648
1649              ai++;
1650          }
1651
1652          cbi->hdr = cbi->stats_freq;
1653          fprintf( stderr, "<RD> listening on port: %s\\n", port );
1654
1655          // create xapp, wait for route table if forwarding
1656          x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
1657
1658          // 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          }
1666
1667          if( ncb < 1 ) {
1668              fprintf( stderr, "<RD> no message types specified on the command line\\n" );
1669              exit( 1 );
1670          }
1671
1672          x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi );        // register default cb
1673
1674          fprintf( stderr, "<RD> starting driver\\n" );
1675          x->Run( nthreads );
1676
1677          // return from run() is not expected, but some compilers might
1678          // compilain if there isn't a return value here.
1679          return 0;
1680      }
1681
1682    Figure 29: Simple callback application.
1683
1684
1685 Callback Receiver
1686 -----------------
1687
1688 This sample programme implements a simple message listener
1689 which registers three callback functions to process two
1690 specific message types and a default callback to handle
1691 unrecognised messages.
1692
1693 When a message of type 1 is received, it will send two
1694 response messages back to the sender. Two messages are sent
1695 in order to illustrate that it is possible to send multiple
1696 responses using the same received message.
1697
1698 The programme illustrates how multiple listening threads can
1699 be used, but the programme is **not** thread safe; to keep
1700 this example as simple as possible, the counters are not
1701 locked when incremented.
1702
1703
1704 Metrics Generation
1705 ------------------
1706
1707 The example also illustrates how a metrics object instance
1708 can be created and used to send appliction metrics to the
1709 collector. In this example the primary callback function will
1710 genereate metrics with the receipt of each 1000th message.
1711
1712
1713    ::
1714
1715      #include <stdio.h>
1716
1717      #include "ricxfcpp/message.hpp"
1718      #include "ricxfcpp/msg_component.hpp"
1719      #include <ricxfcpp/metrics.hpp>
1720      #include "ricxfcpp/xapp.hpp"
1721
1722      // counts; not thread safe
1723      long cb1_count = 0;
1724      long cb2_count = 0;
1725      long cbd_count = 0;
1726
1727      long cb1_lastts = 0;
1728      long cb1_lastc = 0;
1729
1730      /*
1731          Respond with 2 messages for each type 1 received
1732          Send metrics every 1000 messages.
1733      */
1734      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;
1738
1739          // 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" );
1742
1743          cb1_count++;
1744
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          }
1754      }
1755
1756      // 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      }
1761
1762      // 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      }
1767
1768      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;
1773
1774          // very simple flag processing (no bounds/error checking)
1775          while( ai < argc ) {
1776              if( argv[ai][0] != '-' )  {
1777                  break;
1778              }
1779
1780              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;
1785
1786                  case 't':
1787                      nthreads = atoi( argv[ai+1] );
1788                      ai++;
1789                      break;
1790              }
1791
1792              ai++;
1793          }
1794
1795          fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
1796          fprintf( stderr, "<XAPP> starting %d threads\\n", nthreads );
1797
1798          x = new Xapp( port, true );
1799          x->Add_msg_cb( 1, cb1, x );        // register callbacks
1800          x->Add_msg_cb( 2, cb2, NULL );
1801          x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL );
1802
1803          x->Run( nthreads );                // let framework drive
1804          // control should not return
1805      }
1806
1807    Figure 30: Simple callback application.
1808
1809
1810
1811 Looping Sender
1812 --------------
1813
1814 This is another very simple application which demonstrates
1815 how an application can control its own listen loop while
1816 sending messages. As with the other examples, some error
1817 checking is skipped, and short cuts have been made in order
1818 to keep the example small and to the point.
1819
1820
1821    ::
1822
1823
1824      #include <stdio.h>
1825      #include <string.h>
1826      #include <unistd.h>
1827
1828      #include <iostream>
1829      #include <memory>
1830
1831      #include "ricxfcpp/xapp.hpp"
1832
1833      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
1837
1838          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
1847
1848
1849          // very simple flag processing (no bounds/error checking)
1850          while( ai < argc ) {
1851              if( argv[ai][0] != '-' )  {
1852                  break;
1853              }
1854
1855              // 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
1919    Figure 31: Simple looping sender application.
1920
1921
1922
1923 Alarm Generation
1924 ----------------
1925
1926 This is an extension of a previous example which sends an
1927 alarm during initialisation and clears the alarm as soon as
1928 messages are being received. It is unknown if this is the
1929 type of alarm that is expected at the collector, but
1930 illustrates how an alarm is allocated, raised and cleared.
1931
1932
1933    ::
1934
1935
1936      #include <stdio.h>
1937      #include <string.h>
1938      #include <unistd.h>
1939
1940      #include <iostream>
1941      #include <memory>
1942
1943      #include "ricxfcpp/xapp.hpp"
1944      #include "ricxfcpp/alarm.hpp"
1945
1946      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;
1951
1952          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
1962
1963
1964          // very simple flag processing (no bounds/error checking)
1965          while( ai < argc ) {
1966              if( argv[ai][0] != '-' )  {
1967                  break;
1968              }
1969
1970              // 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;
1977
1978                  case 'p':
1979                      port = argv[ai+1];
1980                      ai++;
1981                      break;
1982
1983                  // 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          }
1991
1992          fprintf( stderr, "<XAPP> response timeout set to: %d\\n", response_to );
1993          fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
1994
1995          // 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 );
1998
1999
2000          // 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" );
2003
2004          for( i = 0; i < 100; i++ ) {
2005              mtype++;
2006              if( mtype > 10 ) {
2007                  mtype = 0;
2008              }
2009
2010              // we'll reuse a received message; get max size
2011              sz = msg->Get_available_size();
2012
2013              // 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 );
2016
2017              // 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              }
2021
2022              // 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                  }
2030
2031                  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              }
2038
2039              if( delay > 0 ) {
2040                  usleep( delay );
2041              }
2042          }
2043      }
2044
2045    Figure 32: Simple looping sender application with alarm
2046    generation.
2047
2048
2049
2050 Configuration Interface
2051 -----------------------
2052
2053 This example is a simple illustration of how the
2054 configuration file support (xAPP descriptor) can be used to
2055 suss out configuration parameters before creating the Xapp
2056 object. The example also illustrates how a notification
2057 callback can be used to react to changes in the
2058 configuration.
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.
2139