Add API allowing xAPPs to send alarm messages
[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 xApp
18 object instance, and to use that instance as the logic base.
19 The xApp object provides a message level interface to the RIC
20 Message Router (RMR), including the ability to register
21 callback functions which the instance will drive as messages
22 are received; much in the same way that an X-windows
23 application is driven by the window manager for all activity.
24 The xApp may also choose to use its own send/receive loop,
25 and thus is not required to use the callback driver mechanism
26 provided by the framework.
27
28
29 THE FRAMEWORK API
30 =================
31
32 The C++ framework API consists of the creation of the xApp
33 object, and invoking desired functions via the instance of
34 the object. The following paragraphs cover the various steps
35 involved to create an xApp instance, wait for a route table
36 to arrive, send a message, and wait for messages to arrive.
37
38
39 The Namespace
40 -------------
41
42 Starting with version 2.0.0 the framwork introduces a
43 *namespace* of ``xapp`` for the following classes and types:
44
45
46    * Alarm
47    * Jhash
48    * Message
49    * Msg_component
50
51
52 This is a breaking change and as such the major version was
53 bumpped from 1 to 2.
54
55
56 Creating the xApp instance
57 --------------------------
58
59 The creation of the xApp instance is as simple as invoking
60 the object's constructor with two required parameters:
61
62
63        .. list-table::
64          :widths: auto
65          :header-rows: 0
66          :class: borderless
67
68          * - **port**
69            -
70              A C string (pointer to char) which defines the port that
71              RMR will open to listen for connections.
72
73
74              |
75
76          * - **wait**
77            -
78              A Boolean value which indicates whether or not the
79              initialization process should wait for the arrival of a
80              valid route table before completing. When true is
81              supplied, the initialization will not complete until RMR
82              has received a valid route table (or one is located via
83              the ``RMR_SEED_RT`` environment variable).
84
85
86
87 The following code sample illustrates the simplicity of
88 creating the instance of the xApp object.
89
90
91 ::
92
93       #include <memory>
94       #include <ricxfcpp/xapp.hpp>
95       int main( ) {
96           std::unique_ptr<Xapp> xapp;
97           char* listen_port = (char *) "4560";    //RMR listen port
98           bool  wait4table = true;            // wait for a route table
99
100           xapp = std::unique_ptr<Xapp>(
101                 new Xapp( listen_port, wait4table ) );
102       }
103
104 Figure 1: Creating an xAPP instance.
105
106 From a compilation perspective, the following is the simple
107 compiler invocation string needed to compile and link the
108 above program (assuming that the sample code exists in a file
109 called ``man_ex1.cpp``.
110
111
112 ::
113
114      g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread
115
116
117 The above program, while complete and capable of being
118 compiled, does nothing useful. When invoked, RMR will be
119 initialized and will begin listening for a route table;
120 blocking the return to the main program until one is
121 received. When a valid route table arrives, initialization
122 will complete and the program will exit as there is no code
123 following the instruction to create the object.
124
125
126 LISTENING FOR MESSAGES
127 ======================
128
129 The program in the previous example can be extended with just
130 a few lines of code to enable it to receive and process
131 messages. The application needs to register a callback
132 function for each message type which it desires to process.
133
134 Once registered, each time a message is received the
135 registered callback for the message type will be invoked by
136 the framework.
137
138
139 Callback Signature
140 ------------------
141
142 As with most callback related systems, a callback must have a
143 well known function signature which generally passes event
144 related information and a "user" data pointer which was
145 registered with the function. The following is the prototype
146 which callback functions must be defined with:
147
148
149 ::
150
151       void cb_name( xapp::Message& m, int mtype, int subid,
152             int payload_len, xapp::Msg_component payload,
153             void* usr_data );
154
155 Figure 2: Callback function signature
156
157 The parameters passed to the callback function are as
158 follows:
159
160
161        .. list-table::
162          :widths: auto
163          :header-rows: 0
164          :class: borderless
165
166          * - **m**
167            -
168              A reference to the Message that was received.
169
170
171              |
172
173          * - **mtype**
174            -
175              The message type (allows for disambiguation if the
176              callback is registered for multiple message types).
177
178
179              |
180
181          * - **subid**
182            -
183              The subscription ID from the message.
184
185
186              |
187
188          * - **payload len**
189            -
190              The number of bytes which the sender has placed into the
191              payload.
192
193
194              |
195
196          * - **payload**
197            -
198              A direct reference (smart pointer) to the payload. (The
199              smart pointer is wrapped in a special class in order to
200              provide a custom destruction function without burdening
201              the xApp developer with that knowledge.)
202
203
204              |
205
206          * - **user data**
207            -
208              A pointer to user data. This is the pointer that was
209              provided when the function was registered.
210
211
212
213 To illustrate the use of a callback function, the previous
214 code example has been extended to add the function, register
215 it for message types 1000 and 1001, and to invoke the
216 ``Run()`` function in the framework (explained in the next
217 section).
218
219 ::
220
221       #include <memory>
222       #include <ricxfcpp/xapp.hpp>
223       long m1000_count = 0;    // message counters, one for each type
224       long m1001_count = 0;
225
226       // callback function that will increase the appropriate counter
227       void cbf( xapp::Message& mbuf, int mtype, int subid, int len,
228                   xapp::Msg_component payload,  void* data ) {
229           long* counter;
230
231           if( (counter = (long *) data) != NULL ) {
232               (*counter)++;
233           }
234       }
235
236       int main( ) {
237           std::unique_ptr<Xapp> xapp;
238           char* listen_port = (char *) "4560";
239           bool  wait4table = false;
240
241           xapp = std::unique_ptr<Xapp>(
242                 new Xapp( listen_port, wait4table ) );
243
244           // register the same callback function for both msg types
245           xapp->Add_msg_cb( 1000, cbf, (void *) &m1000_count );
246           xapp->Add_msg_cb( 1001, cbf, (void *) &m1001_count );
247
248           xapp->Run( 1 );        // start the callback driver
249       }
250
251 Figure 3: Callback function example.
252
253 As before, the program does nothing useful, but now it will
254 execute and receive messages. For this example, the same
255 function can be used to increment the appropriate counter
256 simply by providing a pointer to the counter as the user data
257 when the callback function is registered. In addition, a
258 subtle change from the previous example has been to set the
259 wait for table flag to ``false.``
260
261 For an xApp that is a receive only application (never sends)
262 it is not necessary to wait for RMR to receive a table from
263 the Route Manager.
264
265
266 Registering A Default Callback
267 ------------------------------
268
269 The xApp may also register a default callback function such
270 that the function will be invoked for any message that does
271 not have a registered callback. If the xAPP does not register
272 a default callback, any message which cannot be mapped to a
273 known callback function is silently dropped. A default
274 callback is registered by providing a *generic* message type
275 of ``xapp->DEFAULT_CALLBACK`` on an ``Add_msg_cb`` call.
276
277
278 The Framework Callback Driver
279 -----------------------------
280
281 The ``Run()`` function within the Xapp object is invoked to
282 start the callback driver, and the xApp should not expect the
283 function to return under most circumstances. The only
284 parameter that the ``Run()`` function expects is the number
285 of threads to start. For each thread requested, the framework
286 will start a listener thread which will allow received
287 messages to be processed in parallel. If supplying a value
288 greater than one, the xApp must ensure that the callback
289 functions are thread safe as it is very likely that the same
290 callback function will be invoked concurrently from multiple
291 threads.
292
293
294 SENDING MESSAGES
295 ================
296
297 It is very likely that most xApps will need to send messages
298 and will not operate in "receive only" mode. Sending the
299 message is a function of the message object itself and can
300 take one of two forms:
301
302
303    * Replying to the sender of a received message
304
305    * Sending a message (routed based on the message type and
306      subscription ID)
307
308
309 When replying to the sender, the message type and
310 subscription ID are not used to determine the destination of
311 the message; RMR ensures that the message is sent back to the
312 originating xApp. The xApp may still need to change the
313 message type and/or the subscription ID in the message prior
314 to using the reply function.
315
316 To provide for both situations, two reply functions are
317 supported by the Message object as illustrated with the
318 following prototypes.
319
320
321 ::
322
323      bool Send_response(  int mtype, int subid, int response_len,
324           std:shared_ptr<unsigned char> response );
325
326      bool Send_response(  int response_len, std::shared_ptr<unsigned char> response );
327
328 Figure 4: Reply function prototypes.
329
330 In the first prototype the xApp must supply the new message
331 type and subscription ID values, where the second function
332 uses the values which are currently set in the message.
333 Further, the new payload contents, and length, are supplied
334 to both functions; the framework ensures that the message is
335 large enough to accommodate the payload, reallocating it if
336 necessary, and copies the response into the message payload
337 prior to sending. Should the xApp need to change either the
338 message type, or the subscription ID, but not both, the
339 ``NO_CHANGE`` constant can be used as illustrated below.
340
341
342 ::
343
344       msg->Send_response( xapp::Message::NO_CHANGE, xapp::Message::NO_SUBID,
345           pl_length, (unsigned char *) payload );
346
347 Figure 5: Send response prototype.
348
349 In addition to the two function prototypes for
350 ``Send_response()`` there are two additional prototypes which
351 allow the new payload to be supplied as a shared smart
352 pointer. The other parameters to these functions are
353 identical to those illustrated above, and thus are not
354 presented here.
355
356 The ``Send_msg()`` set of functions supported by the Message
357 object are identical to the ``Send_response()`` functions and
358 are shown below.
359
360
361 ::
362
363       bool Send_msg( int mtype, int subid, int payload_len,
364           std::shared_ptr<unsigned char> payload );
365
366       bool Send_msg( int mtype, int subid, int payload_len,
367           unsigned char* payload );
368
369       bool Send_msg( int payload_len,
370           std::shared_ptr<unsigned char> payload );
371
372       bool Send_msg( int payload_len, unsigned char* payload );
373
374 Figure 6: Send function prototypes.
375
376 Each send function accepts the message, copies in the payload
377 provided, sets the message type and subscription ID (if
378 provided), and then causes the message to be sent. The only
379 difference between the ``Send_msg()`` and
380 ``Send_response()`` functions is that the destination of the
381 message is selected based on the mapping of the message type
382 and subscription ID using the current routing table known to
383 RMR.
384
385
386 Direct Payload Manipulation
387 ---------------------------
388
389 For some applications, it might be more efficient to
390 manipulate the payload portion of an Xapp Message in place,
391 rather than creating it and relying on a buffer copy when the
392 message is finally sent. To achieve this, the xApp must
393 either use the smart pointer to the payload passed to the
394 callback function, or retrieve one from the message using
395 ``Get_payload()`` when working with a message outside of a
396 callback function. Once the smart pointer is obtained, the
397 pointer's get() function can be used to directly reference
398 the payload (unsigned char) bytes.
399
400 When working directly with the payload, the xApp must take
401 care not to write more than the actual payload size which can
402 be extracted from the Message object using the
403 ``Get_available_size()`` function.
404
405 When sending a message where the payload has been directly
406 altered, and no extra buffer copy is needed, a NULL pointer
407 should be passed to the Message send function. The following
408 illustrates how the payload can be directly manipulated and
409 returned to the sender (for simplicity, there is no error
410 handling if the payload size of the received message isn't
411 large enough for the response string, the response is just
412 not sent).
413
414
415 ::
416
417       Msg_component payload;  // smart reference
418       int pl_size;            // max size of payload
419
420       payload = msg->Get_payload();
421       pl_size = msg->Get_available_size();
422       if( snprintf( (char *) payload.get(), pl_size,
423           "Msg Received\\n" ) < pl_size ) {
424         msg->Send_response( M_TYPE, SID, strlen( raw_pl ), NULL );
425       }
426
427 Figure 7: Send message without buffer copy.
428
429
430
431 Sending Multiple Responses
432 --------------------------
433
434 It is likely that the xApp will wish to send multiple
435 responses back to the process that sent a message that
436 triggered the callback. The callback function may invoke the
437 ``Send_response()`` function multiple times before returning.
438
439 After each call, the Message retains the necessary
440 information to allow for a subsequent invocation to send more
441 data. It should be noted though, that after the first call to
442 ``{Send_response()`` the original payload will be lost; if
443 necessary, the xApp must make a copy of the payload before
444 the first response call is made.
445
446
447 Message Allocation
448 ------------------
449
450 Not all xApps will be "responders," meaning that some xApps
451 will need to send one or more messages before they can expect
452 to receive any messages back. To accomplish this, the xApp
453 must first allocate a message buffer, optionally initialising
454 the payload, and then using the message's ``Send_msg()``
455 function to send a message out. The framework's
456 ``Alloc_msg()`` function can be used to create a Message
457 object with a desired payload size.
458
459
460 FRAMEWORK PROVIDED CALLBACKS
461 ============================
462
463 The framework itself may provide message handling via the
464 driver such that the xApp might not need to implement some
465 message processing functionality. Initially, the C++
466 framework will provide a default callback function to handle
467 the RMR based health check messages. This callback function
468 will assume that if the message was received, and the
469 callback invoked, that all is well and will reply with an OK
470 state. If the xApp should need to override this simplistic
471 response, all it needs to do is to register its own callback
472 function for the health check message type.
473
474
475 JSON SUPPORT
476 ============
477
478 The C++ xAPP framework provides a very lightweight json
479 parser and data hash facility. Briefly, a json hash (Jhash)
480 can be established by creating an instance of the Jhash
481 object with a string of valid json. The resulting object's
482 functions can then be used to read values from the resulting
483 hash.
484
485
486 Creating The Jhash Object
487 -------------------------
488
489 The Jhash object is created simply by passing a json string
490 to the constructor.
491
492 ::
493
494       #include <ricxfcpp/Jhash>
495
496       std::string jstring = "{ \\"tag\\": \\"Hello World\\" }";
497       Jhash*  jh;
498
499       jh =  new Jhash( jstring.c_str() );
500
501 Figure 8: The creation of the Jhash object.
502
503 Once the Jhash object has been created any of the methods
504 described in the following paragraphs can be used to retrieve
505 the data:
506
507
508 Json Blobs
509 ----------
510
511 Json objects can be nested, and the nesting is supported by
512 this representation. The approach taken by Jhash is a
513 "directory view" approach, where the "current directory," or
514 current *blob,* limits the scope of visible fields.
515
516 As an example, the json contained in figure 9, contains a
517 "root" blob and two *sub-blobs* (address and lease_info).
518
519
520 ::
521
522       {
523           "lodge_name": "Water Buffalo Lodge 714",
524           "member_count": 41,
525           "grand_poobah": "Larry K. Slate",
526           "attendance":   [ 23, 14, 41, 38, 24 ],
527           "address": {
528               "street":    "16801 Stonway Lane",
529               "suite":     null,
530               "city":      "Bedrock",
531               "post_code": "45701"
532           },
533           "lease_info": {
534               "owner":    "Stonegate Properties",
535               "amount":   216.49,
536               "due":      "monthly",
537               "contact:"  "Kyle Limestone"
538           }
539       }
540
541 Figure 9: Sample json with a root and two blobs.
542
543 Upon creation of the Jhash object, the *root* fields,
544 ``lodge_name,`` ``member_count,`` and ``grand_poobah`` are
545 immediately available. The fields in the *sub-blobs* are
546 available only when the correct blob is selected. The code
547 sample in figure 10 illustrates how a *sub-blob* is selected.
548
549 ::
550
551       jh->Set_blob( (char *) "address" );     // select address
552       jh->Unset_blob();                       // return to root
553       jh->Set_blob( (char *) "lease_info" );  // select the lease blob
554
555 Figure 10: Blob selection example.
556
557 Currently, the selected blob must be unset in order to select
558 a blob at the root level; unset always sets the root blob.
559 Attempting to use the ``Set_blob`` function will attempt to
560 select the named blob from the current blob, and not the
561 root.
562
563
564 Simple Value Extraction
565 -----------------------
566
567 Simple values are the expected data types *string, value,*
568 and *boolean.* This lightweight json parser treats all values
569 as floating point numbers and does not attempt to maintain a
570 separate integer type. A fourth type, *null,* is supported to
571 allow the user to expressly check for a field which is
572 defined but has no value; as opposed to a field that was
573 completely missing from the data. The following are the
574 prototypes for the functions which allow values to be
575 extracted:
576
577
578 ::
579
580       std::string String( const char* name );
581       float Value( const char* name );
582       bool Bool( const char* name );
583
584
585 Each of these functions returns the value associated with the
586 field with the given *name.* If the value is missing, the
587 following default values are returned:
588
589
590        .. list-table::
591          :widths: 15,80
592          :header-rows: 0
593          :class: borderless
594
595          * - **String**
596            -
597              An empty string (.e.g "").
598
599              |
600
601          * - **Value**
602            -
603              Zero (e.g 0.0)
604
605              |
606
607          * - **bool**
608            -
609              false
610
611
612
613 If the user needs to disambiguate between a missing value and
614 the default value either the ``Missing`` or ``Exists``
615 function should be used first.
616
617
618 Testing For Existing and Missing Fields
619 ---------------------------------------
620
621 Two functions allow the developer to determine whether or not
622 a field is included in the json. Both of these functions work
623 on the current *blob,* therefore it is important to ensure
624 that the correct blob is selected before using either of
625 these functions. The prototypes for the ``Exists`` and
626 ``Missing`` functions are below:
627
628 ::
629
630       bool Exists( const char* name );
631       bool Is_missing( const char* name );
632
633 The ``Exists`` function returns *true* if the field name
634 exists in the json and *false* otherwise. Conversely, the
635 ``Missing`` function returns *true* when the field name does
636 not exist in the json.
637
638
639 Testing Field Type
640 ------------------
641
642 The ``Exists`` and ``Missing`` functions might not be enough
643 for the user code to validate the data that it has. To assist
644 with this, several functions allow direct type testing on a
645 field in the current blob. The following are the prototypes
646 for these functions:
647
648 ::
649
650       bool Is_bool( const char* name );
651       bool Is_null( const char* name );
652       bool Is_string( const char* name );
653       bool Is_value( const char* name );
654
655
656 Each of these functions return *true* if the field with the
657 given name is of the type being tested for.
658
659
660 Arrays
661 ------
662
663 Arrays are supported in the same manner as simple field
664 values with the addition of the need to supply an array index
665 when fetching values from the object. In addition, there is a
666 *length* function which can be used to determine the number
667 of elements in the named array. The prototypes for the array
668 based functions are below:
669
670 ::
671
672       int Array_len( const char* name );
673
674       bool Is_bool_ele( const char* name, int eidx );
675       bool Is_null_ele( const char* name, int eidx );
676       bool Is_string_ele( const char* name, int eidx );
677       bool Is_value_ele( const char* name, int eidx );
678
679       bool Bool_ele( const char* name, int eidx );
680       std::string String_ele( const char* name, int eidx );
681       float Value_ele( const char* name, int eidx );
682
683
684 For each of these functions the ``eidx`` is the zero based
685 element index which is to be tested or selected.
686
687
688 Arrays of Blobs
689 ---------------
690
691 An array containing blobs, rather than simple field value
692 pairs, the blob must be selected prior to using it, just as a
693 sub-blob needed to be selected. The ``Set_blob_ele`` function
694 is used to do this and has the following prototype:
695
696 ::
697
698       bool Set_blob_ele( const char* name, int eidx );
699
700
701 As with selecting a sub-blob, an unset must be performed
702 before selecting the next blob. Figure 11 illustrates how
703 these functions can be used to read and print values from the
704 json in figure 12.
705
706 ::
707
708       "members": [
709           { "name": "Fred Flinstone", "member_num": 42 },
710           { "name": "Barney Rubble", "member_num": 48 },
711           { "name": "Larry K Slate", "member_num": 22 },
712           { "name": "Kyle Limestone", "member_num": 49 }
713       ]
714
715 Figure 11: Json array containing blobs.
716
717
718 ::
719
720       std::string mname;
721       float mnum;
722       int len;
723
724       len = jh->Array_len( (char *) "members" );
725       for( i = 0; i < len; i++ ) {
726           jh->Set_blob_ele( (char *) "members", i );  // select blob
727
728           mname = jh->String( (char *) "name" );      // read values
729           mnum = jh->Value( (char *) "member_num" );
730           fprintf( stdout, "%s is member %d\\n", mname.c_str(), (int) mnum );
731
732           jh->Unset_blob();                           // back to root
733       }
734
735 Figure 12: Code to process the array of blobs.
736
737
738
739 ALARM MANAGER INTERFACE
740 =======================
741
742 The C++ framework provides an API which allows the xAPP to
743 easily construct and generate alarm messages. Alarm messages
744 are a special class of RMR message, allocated in a similar
745 fashion as an RMR message through the framework's
746 ``Alloc_alarm()`` function.
747
748 The API consists of the following function types:
749
750
751        .. list-table::
752          :widths: auto
753          :header-rows: 0
754          :class: borderless
755
756          * - **Raise**
757            -
758              Cause the alarm to be assigned a severity and and sent via
759              RMR message to the alarm collector process.
760
761
762              |
763
764          * - **Clear**
765            -
766              Cause a clear message to be sent to the alarm collector.
767
768
769              |
770
771          * - **Raise Again**
772            -
773              Cause a clear followed by a raise message to be sent to
774              the alarm collector.
775
776
777
778
779
780 Allocating Alarms
781 -----------------
782
783 The ``xapp`` function provided by the framework is used to
784 create an alarm object. Once the xAPP has an alarm object it
785 can be used to send one, or more, alarm messages to the
786 collector.
787
788 The allocation function has three prototypes which allow the
789 xAPP to create an alarm with an initial set of information as
790 is appropriate. The following are the prototypes for the
791 allocate functions:
792
793
794 ::
795
796     std::unique_ptr<xapp::Alarm> Alloc_alarm( );
797     std::unique_ptr<xapp::Alarm> Alloc_alarm( std::string meid );
798     std::unique_ptr<xapp::Alarm> Alloc_alarm( int prob_id, std::string meid );
799
800 Figure 13: Alarm allocation prototypes.
801
802 Each of the allocation functions returns a unique pointer to
803 the alarm. In the simplest form (1) the alarm is initialised
804 with an empty meid (managed element ID) string, and unset
805 problem ID (-1). The second prototype allows the xAPP to
806 supply the meid, and in the third form both the problem ID
807 and the meid are used to initialise the alarm.
808
809
810 Raising An Alarm
811 ----------------
812
813 Once an alarm has been allocated, its ``Raise()`` function
814 can be used to cause the alarm to be sent to the collector.
815 The raise process allows the xAPP to perform the following
816 modifications to the alarm before sending the message:
817
818
819    * Set the alarm severity
820
821    * Set the problem ID value
822
823    * Set the alarm information string
824
825    * Set the additional information string
826
827
828 The following are the prototypes for the ``Raise()``
829 functions of an alarm object: ..... In its simplest form (1)
830 the ``Raise()`` function will send the alarm without making
831 any changes to the data. The final two forms allow the xAPP
832 to supply additional data which is added to the alarm before
833 sending the message. Each of the raise functions returns
834 ``true`` on success and ``false`` if the alarm message could
835 not be sent.
836
837
838 Severity
839 --------
840
841 The severity is one of the ``SEV_`` constants listed below.
842 These map to alarm collector strings and insulate the xAPP
843 from any future alarm collector changes. The specific meaning
844 of these severity types are defined by the alarm collector
845 and thus no attempt is made to guess what their actual
846 meaning is. These constants are available by including
847 ``alarm.hpp.``
848
849
850    ::
851
852          SEV_MAJOR
853          SEV_MINOR
854          SEV_WARN
855          SEV_DEFAULT
856
857 Figure 14: Severity constants available in alarm.hpp.
858
859
860 The Problem ID
861 --------------
862
863 The problem ID is an integer which is assigned by the xAPP.
864 The framework makes no attempt to verify that it has been se,
865 nor does it attempt to validate the value. If the xAPP does
866 not set the value, ``-1`` is used.
867
868
869 Information Strings
870 -------------------
871
872 The two information strings are also xAPP defined and provide
873 the information that the xAPP deems necessary and related to
874 the alarm. What the collector expects, and how these strings
875 are used, is beyond the scope of the framework to describe or
876 validate. If not supplied, empty strings are sent in the
877 alarm message.
878
879
880 Clearing An Alarm
881 -----------------
882
883 The ``Clear()`` function of an alarm may be used to send a
884 clear message. In a manner similar to the ``Raise()``
885 functions, the ``Clear()`` functions allow the existing alarm
886 data to be sent without change, or for the xAPP to modify the
887 data before the message is sent to the collector. The
888 following are the prototype for these functions.
889
890 ::
891
892      bool Clear( );
893      bool Clear( int severity, int problem, std::string info );
894      bool Clear( int severity, int problem, std::string info, std::string addional_info );
895      bool Clear_all( );
896
897
898 Figure 15: Clear function prototypes.
899
900 Each of the clear functions returns ``true`` on success and
901 ``false`` if the alarm message could not be sent.
902
903 The ``Clear_all()`` function sends a special action code to
904 the collector which is assumed to clear all alarms. However,
905 it is unknown whether that implies **all** alarms, or all
906 alarms matching the ``problem_id,`` or some other
907 interpretation. Please consult the alarm collector
908 documentation for these specifics.
909
910
911 Adjusting Alarm Contents
912 ------------------------
913
914 It might be necessary for the xAPP to adjust the alarm
915 contents outside of the scope of the ``Raise()`` function, or
916 to adjust data that cannot be manipulated by ``Raise().`` The
917 following are the (self explanatory) prototypes for the
918 *setter* functions which are available to the xAPP.
919
920
921 ::
922
923     void Set_additional( std::string new_info );
924     void Set_appid( std::string new_id );
925     void Set_info( std::string new_info );
926     void Set_meid( std::string new_meid );
927     void Set_problem( int new_id );
928     void Set_severity( int new_sev );
929
930 Figure 16: Alarm Setters
931
932
933
934 EXAMPLE PROGRAMMES
935 ==================
936
937 The following sections contain several example programmes
938 which are written on top of the C++ framework.
939
940
941 RMR Dump xAPP
942 -------------
943
944 The RMR dump application is an example built on top of the
945 C++ xApp framework to both illustrate the use of the
946 framework, and to provide a useful diagnostic tool when
947 testing and troubleshooting xApps.
948
949 The RMR dump xApp isn't a traditional xApp inasmuch as its
950 goal is to listen for message types and to dump information
951 about the messages received to the TTY much as
952 ``tcpdump`` does for raw packet traffic. The full source
953 code, and Makefile, are in the ``examples`` directory of the
954 C++ framework repo.
955
956 When invoked, the RMR dump program is given one or more
957 message types to listen for. A callback function is
958 registered for each, and the framework ``Run()`` function is
959 invoked to drive the process. For each recognised message,
960 and depending on the verbosity level supplied at program
961 start, information about the received message(s) is written
962 to the TTY. If the forwarding option, -f, is given on the
963 command line, and an appropriate route table is provided,
964 each received message is forwarded without change. This
965 allows for the insertion of the RMR dump program into a flow,
966 however if the ultimate receiver of a message needs to reply
967 to that message, the reply will not reach the original
968 sender, so RMR dump is not a complete "middle box"
969 application.
970
971 The following is the code for this xAPP. Several functions,
972 which provide logic unrelated to the framework, have been
973 omitted. The full code is in the framework repository.
974
975
976
977    ::
978
979      #include <stdio.h>
980      #include <unistd.h>
981      #include <atomic>
982
983      #include "ricxfcpp/xapp.hpp"
984
985      /*
986          Information that the callback needs outside
987          of what is given to it via parms on a call
988          by the framework.
989      */
990      typedef struct {
991          int        vlevel;             // verbosity level
992          bool    forward;            // if true, message is forwarded
993          int        stats_freq;         // header/stats after n messages
994          std::atomic<long>    pcount; // messages processed
995          std::atomic<long>    icount; // messages ignored
996          std::atomic<int>    hdr;    // number of messages before next header
997      } cb_info_t;
998
999      // ----------------------------------------------------------------------
1000
1001      /*
1002          Dump bytes to tty.
1003      */
1004      void dump( unsigned const char* buf, int len ) {
1005          int        i;
1006          int        j;
1007          char    cheater[17];
1008
1009          fprintf( stdout, "<RD> 0000 | " );
1010          j = 0;
1011          for( i = 0; i < len; i++ ) {
1012              cheater[j++] =  isprint( buf[i] ) ? buf[i] : '.';
1013              fprintf( stdout, "%02x ", buf[i] );
1014
1015              if( j == 16 ) {
1016                  cheater[j] = 0;
1017                  fprintf( stdout, " | %s\\n<RD> %04x | ", cheater, i+1 );
1018                  j = 0;
1019              }
1020          }
1021
1022          if( j ) {
1023              i = 16 - (i % 16);
1024              for( ; i > 0; i-- ) {
1025                  fprintf( stdout, "   " );
1026              }
1027              cheater[j] = 0;
1028              fprintf( stdout, " | %s\\n", cheater );
1029          }
1030      }
1031
1032      /*
1033          generate stats when the hdr count reaches 0. Only one active
1034          thread will ever see it be exactly 0, so this is thread safe.
1035      */
1036      void stats( cb_info_t& cbi ) {
1037          int curv;                    // current stat trigger value
1038
1039          curv = cbi.hdr--;
1040
1041          if( curv == 0 ) {                    // stats when we reach 0
1042              fprintf( stdout, "ignored: %ld  processed: %ld\\n",
1043                  cbi.icount.load(), cbi.pcount.load() );
1044              if( cbi.vlevel > 0 ) {
1045                  fprintf( stdout, "\\n     %5s %5s %2s %5s\\n",
1046                      "MTYPE", "SUBID", "ST", "PLLEN" );
1047              }
1048
1049              cbi.hdr = cbi.stats_freq;        // reset must be last
1050          }
1051      }
1052
1053      void cb1( xapp::Message& mbuf, int mtype, int subid, int len,
1054                      xapp::Msg_component payload,  void* data ) {
1055          cb_info_t*    cbi;
1056          long total_count;
1057
1058          if( (cbi = (cb_info_t *) data) == NULL ) {
1059              return;
1060          }
1061
1062          cbi->pcount++;
1063          stats( *cbi );            // gen stats & header if needed
1064
1065          if( cbi->vlevel > 0 ) {
1066              fprintf( stdout, "<RD> %-5d %-5d %02d %-5d \\n",
1067                      mtype, subid, mbuf.Get_state(), len );
1068
1069              if( cbi->vlevel > 1 ) {
1070                  dump(  payload.get(), len > 64 ? 64 : len );
1071              }
1072          }
1073
1074          if( cbi->forward ) {
1075              // forward with no change to len or payload
1076              mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL );
1077          }
1078      }
1079
1080      /*
1081          registered as the default callback; it counts the
1082          messages that we aren't giving details about.
1083      */
1084      void cbd( xapp::Message& mbuf, int mtype, int subid, int len,
1085                      xapp::Msg_component payload,  void* data ) {
1086          cb_info_t*    cbi;
1087
1088          if( (cbi = (cb_info_t *) data) == NULL ) {
1089              return;
1090          }
1091
1092          cbi->icount++;
1093          stats( *cbi );
1094
1095          if( cbi->forward ) {
1096              // forward with no change to len or payload
1097              mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL );
1098          }
1099      }
1100
1101      int main( int argc, char** argv ) {
1102          std::unique_ptr<Xapp> x;
1103          char*    port = (char *) "4560";
1104          int ai = 1;                    // arg processing index
1105          cb_info_t*    cbi;
1106          int        ncb = 0;            // number of callbacks registered
1107          int        mtype;
1108          int        nthreads = 1;
1109
1110          cbi = (cb_info_t *) malloc( sizeof( *cbi ) );
1111          cbi->pcount = 0;
1112          cbi->icount = 0;
1113          cbi->stats_freq = 10;
1114
1115          ai = 1;
1116          // very simple flag parsing (no error/bounds checking)
1117          while( ai < argc ) {
1118              if( argv[ai][0] != '-' )  {        // break on first non-flag
1119                  break;
1120              }
1121
1122              // very simple arg parsing; each must be separate -x -y not -xy.
1123              switch( argv[ai][1] ) {
1124                  case 'f':                    // enable packet forwarding
1125                      cbi->forward = true;
1126                      break;
1127
1128                  case 'p':                    // define port
1129                      port = argv[ai+1];
1130                      ai++;
1131                      break;
1132
1133                  case 's':                        // stats frequency
1134                      cbi->stats_freq = atoi( argv[ai+1] );
1135                      if( cbi->stats_freq < 5 ) {    // enforce sanity
1136                          cbi->stats_freq = 5;
1137                      }
1138                      ai++;
1139                      break;
1140
1141                  case 't':                        // thread count
1142                      nthreads = atoi( argv[ai+1] );
1143                      if( nthreads < 1 ) {
1144                          nthreads = 1;
1145                      }
1146                      ai++;
1147                      break;
1148
1149                  case 'v':            // simple verbose bump
1150                      cbi->vlevel++;
1151                      break;
1152
1153                  case 'V':            // explicit verbose level
1154                      cbi->vlevel = atoi( argv[ai+1] );
1155                      ai++;
1156                      break;
1157
1158                  default:
1159                      fprintf( stderr, "unrecognised option: %s\\n", argv[ai] );
1160                      fprintf( stderr, "usage: %s [-f] [-p port] "
1161                                      "[-s stats-freq]  [-t thread-count] "
1162                                      "[-v | -V n] msg-type1 ... msg-typen\\n",
1163                                      argv[0] );
1164                      fprintf( stderr, "\\tstats frequency is based on # of messages received\\n" );
1165                      fprintf( stderr, "\\tverbose levels (-V) 0 counts only, "
1166                                      "1 message info 2 payload dump\\n" );
1167                      exit( 1 );
1168              }
1169
1170              ai++;
1171          }
1172
1173          cbi->hdr = cbi->stats_freq;
1174          fprintf( stderr, "<RD> listening on port: %s\\n", port );
1175
1176          // create xapp, wait for route table if forwarding
1177          x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
1178
1179          // register callback for each type on the command line
1180          while( ai < argc ) {
1181              mtype = atoi( argv[ai] );
1182              ai++;
1183              fprintf( stderr, "<RD> capturing messages for type %d\\n", mtype );
1184              x->Add_msg_cb( mtype, cb1, cbi );
1185              ncb++;
1186          }
1187
1188          if( ncb < 1 ) {
1189              fprintf( stderr, "<RD> no message types specified on the command line\\n" );
1190              exit( 1 );
1191          }
1192
1193          x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi );        // register default cb
1194
1195          fprintf( stderr, "<RD> starting driver\\n" );
1196          x->Run( nthreads );
1197
1198          // return from run() is not expected, but some compilers might
1199          // compilain if there isn't a return value here.
1200          return 0;
1201      }
1202
1203    Figure 17: Simple callback application.
1204
1205
1206 Callback Receiver
1207 -----------------
1208
1209 This sample programme implements a simple message listener
1210 which registers three callback functions to process two
1211 specific message types and a default callback to handle
1212 unrecognised messages.
1213
1214 When a message of type 1 is received, it will send two
1215 response messages back to the sender. Two messages are sent
1216 in order to illustrate that it is possible to send multiple
1217 responses using the same received message.
1218
1219 The programme illustrates how multiple listening threads can
1220 be used, but the programme is **not** thread safe; to keep
1221 this example as simple as possible, the counters are not
1222 locked when incremented.
1223
1224
1225    ::
1226
1227      #include <stdio.h>
1228
1229      #include "ricxfcpp/message.hpp"
1230      #include "ricxfcpp/msg_component.hpp"
1231      #include "ricxfcpp/xapp.hpp"
1232
1233      // counts; not thread safe
1234      long cb1_count = 0;
1235      long cb2_count = 0;
1236      long cbd_count = 0;
1237
1238      long cb1_lastts = 0;
1239      long cb1_lastc = 0;
1240
1241      // respond with 2 messages for each type 1 received
1242      void cb1( xapp::Message& mbuf, int mtype, int subid, int len,
1243                  xapp::Msg_component payload,  void* data ) {
1244          long now;
1245          long total_count;
1246
1247          // illustrate that we can use the same buffer for 2 rts calls
1248          mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK1\\n" );
1249          mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\\n" );
1250
1251          cb1_count++;
1252      }
1253
1254      // just count messages
1255      void cb2( xapp::Message& mbuf, int mtype, int subid, int len,
1256                  xapp::Msg_component payload,  void* data ) {
1257          cb2_count++;
1258      }
1259
1260      // default to count all unrecognised messages
1261      void cbd( xapp::Message& mbuf, int mtype, int subid, int len,
1262                  xapp::Msg_component payload,  void* data ) {
1263          cbd_count++;
1264      }
1265
1266      int main( int argc, char** argv ) {
1267          Xapp* x;
1268          char*    port = (char *) "4560";
1269          int ai = 1;                            // arg processing index
1270          int nthreads = 1;
1271
1272          // very simple flag processing (no bounds/error checking)
1273          while( ai < argc ) {
1274              if( argv[ai][0] != '-' )  {
1275                  break;
1276              }
1277
1278              switch( argv[ai][1] ) {            // we only support -x so -xy must be -x -y
1279                  case 'p':
1280                      port = argv[ai+1];
1281                      ai++;
1282                      break;
1283
1284                  case 't':
1285                      nthreads = atoi( argv[ai+1] );
1286                      ai++;
1287                      break;
1288              }
1289
1290              ai++;
1291          }
1292
1293          fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
1294          fprintf( stderr, "<XAPP> starting %d threads\\n", nthreads );
1295
1296          x = new Xapp( port, true );
1297          x->Add_msg_cb( 1, cb1, NULL );                // register callbacks
1298          x->Add_msg_cb( 2, cb2, NULL );
1299          x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL );
1300
1301          x->Run( nthreads );                // let framework drive
1302          // control should not return
1303      }
1304
1305    Figure 18: Simple callback application.
1306
1307
1308
1309 Looping Sender
1310 --------------
1311
1312 This is another very simple application which demonstrates
1313 how an application can control its own listen loop while
1314 sending messages. As with the other examples, some error
1315 checking is skipped, and short cuts have been made in order
1316 to keep the example small and to the point.
1317
1318
1319    ::
1320
1321
1322      #include <stdio.h>
1323      #include <string.h>
1324      #include <unistd.h>
1325
1326      #include <iostream>
1327      #include <memory>
1328
1329      #include "ricxfcpp/xapp.hpp"
1330
1331      extern int main( int argc, char** argv ) {
1332          std::unique_ptr<Xapp> xfw;
1333          std::unique_ptr<xapp::Message> msg;
1334          xapp::Msg_component payload;                // special type of unique pointer to the payload
1335
1336          int    sz;
1337          int len;
1338          int i;
1339          int ai;
1340          int response_to = 0;                // max timeout wating for a response
1341          char*    port = (char *) "4555";
1342          int    mtype = 0;
1343          int rmtype;                            // received message type
1344          int delay = 1000000;                // mu-sec delay; default 1s
1345
1346
1347          // very simple flag processing (no bounds/error checking)
1348          while( ai < argc ) {
1349              if( argv[ai][0] != '-' )  {
1350                  break;
1351              }
1352
1353              // we only support -x so -xy must be -x -y
1354              switch( argv[ai][1] ) {
1355                  // delay between messages (mu-sec)
1356                  case 'd':
1357                      delay = atoi( argv[ai+1] );
1358                      ai++;
1359                      break;
1360
1361                  case 'p':
1362                      port = argv[ai+1];
1363                      ai++;
1364                      break;
1365
1366                  // timeout in seconds; we need to convert to ms for rmr calls
1367                  case 't':
1368                      response_to = atoi( argv[ai+1] ) * 1000;
1369                      ai++;
1370                      break;
1371              }
1372              ai++;
1373          }
1374
1375          fprintf( stderr, "<XAPP> response timeout set to: %d\\n", response_to );
1376          fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
1377
1378          // get an instance and wait for a route table to be loaded
1379          xfw = std::unique_ptr<Xapp>( new Xapp( port, true ) );
1380          msg = xfw->Alloc_msg( 2048 );
1381
1382          for( i = 0; i < 100; i++ ) {
1383              mtype++;
1384              if( mtype > 10 ) {
1385                  mtype = 0;
1386              }
1387
1388              // we'll reuse a received message; get max size
1389              sz = msg->Get_available_size();
1390
1391              // direct access to payload; add something silly
1392              payload = msg->Get_payload();
1393              len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i );
1394
1395              // payload updated in place, prevent copy by passing nil
1396              if ( ! msg->Send_msg( mtype, xapp::Message::NO_SUBID,  len, NULL )) {
1397                  fprintf( stderr, "<SNDR> send failed: %d\\n", i );
1398              }
1399
1400              // receive anything that might come back
1401              msg = xfw->Receive( response_to );
1402              if( msg != NULL ) {
1403                  rmtype = msg->Get_mtype();
1404                  payload = msg->Get_payload();
1405                  fprintf( stderr, "got: mtype=%d payload=(%s)\\n",
1406                      rmtype, (char *) payload.get() );
1407              } else {
1408                  msg = xfw->Alloc_msg( 2048 );
1409              }
1410
1411              if( delay > 0 ) {
1412                  usleep( delay );
1413              }
1414          }
1415      }
1416
1417    Figure 19: Simple looping sender application.
1418
1419
1420
1421 Alarm Example
1422 -------------
1423
1424    This is an extension of a previous example which sends an
1425    alarm during initialisation and clears the alarm as soon
1426    as messages are being received. It is unknown if this is
1427    the type of alarm that is expected at the collector, but
1428    illustrates how an alarm is allocated, raised and cleared.
1429
1430
1431       ::
1432
1433
1434         #include <stdio.h>
1435         #include <string.h>
1436         #include <unistd.h>
1437
1438         #include <iostream>
1439         #include <memory>
1440
1441         #include "ricxfcpp/xapp.hpp"
1442         #include "ricxfcpp/alarm.hpp"
1443
1444         extern int main( int argc, char** argv ) {
1445             std::unique_ptr<Xapp> xfw;
1446             std::unique_ptr<xapp::Message> msg;
1447             xapp::Msg_component payload;                // special type of unique pointer to the payload
1448             std::unique_ptr<xapp::Alarm>    alarm;
1449
1450             bool received = false;                // false until we've received a message
1451             int    sz;
1452             int len;
1453             int i;
1454             int ai = 1;
1455             int response_to = 0;                // max timeout wating for a response
1456             char*    port = (char *) "4555";
1457             int    mtype = 0;
1458             int rmtype;                            // received message type
1459             int delay = 1000000;                // mu-sec delay; default 1s
1460
1461
1462             // very simple flag processing (no bounds/error checking)
1463             while( ai < argc ) {
1464                 if( argv[ai][0] != '-' )  {
1465                     break;
1466                 }
1467
1468                 // we only support -x so -xy must be -x -y
1469                 switch( argv[ai][1] ) {
1470                     // delay between messages (mu-sec)
1471                     case 'd':
1472                         delay = atoi( argv[ai+1] );
1473                         ai++;
1474                         break;
1475
1476                     case 'p':
1477                         port = argv[ai+1];
1478                         ai++;
1479                         break;
1480
1481                     // timeout in seconds; we need to convert to ms for rmr calls
1482                     case 't':
1483                         response_to = atoi( argv[ai+1] ) * 1000;
1484                         ai++;
1485                         break;
1486                 }
1487                 ai++;
1488             }
1489
1490             fprintf( stderr, "<XAPP> response timeout set to: %d\\n", response_to );
1491             fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
1492
1493             // get an instance and wait for a route table to be loaded
1494             xfw = std::unique_ptr<Xapp>( new Xapp( port, true ) );
1495             msg = xfw->Alloc_msg( 2048 );
1496
1497
1498             // raise an unavilable alarm which we'll clear on the first recevied message
1499             alarm =  xfw->Alloc_alarm( "meid-1234"  );
1500             alarm->Raise( xapp::Alarm::SEV_MINOR, 13, "unavailable", "no data recevied" );
1501
1502             for( i = 0; i < 100; i++ ) {
1503                 mtype++;
1504                 if( mtype > 10 ) {
1505                     mtype = 0;
1506                 }
1507
1508                 // we'll reuse a received message; get max size
1509                 sz = msg->Get_available_size();
1510
1511                 // direct access to payload; add something silly
1512                 payload = msg->Get_payload();
1513                 len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i );
1514
1515                 // payload updated in place, prevent copy by passing nil
1516                 if ( ! msg->Send_msg( mtype, xapp::Message::NO_SUBID,  len, NULL )) {
1517                     fprintf( stderr, "<SNDR> send failed: %d\\n", i );
1518                 }
1519
1520                 // receive anything that might come back
1521                 msg = xfw->Receive( response_to );
1522                 if( msg != NULL ) {
1523                     if( ! received ) {
1524                         alarm->Clear( xapp::Alarm::SEV_MINOR, 13, "messages flowing", "" );                // clear the alarm on first received message
1525                         received = true;
1526                     }
1527
1528                     rmtype = msg->Get_mtype();
1529                     payload = msg->Get_payload();
1530                     fprintf( stderr, "got: mtype=%d payload=(%s)\\n",
1531                         rmtype, (char *) payload.get() );
1532                 } else {
1533                     msg = xfw->Alloc_msg( 2048 );
1534                 }
1535
1536                 if( delay > 0 ) {
1537                     usleep( delay );
1538                 }
1539             }
1540         }
1541
1542       Figure 20: Simple looping sender application with alarm
1543       generation.
1544