Add metrics to the framework
[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 METRICS SUPPORT
935 ===============
936
937 The C++ xAPP framework provides a lightweight interface to
938 the metrics gateway allowing the xAPP to create and send
939 metrics updates without needing to understand the underlying
940 message format. From the xAPP's perspective, the metrics
941 object is created with one or more key/value measurement
942 pairs and then is sent to the process responsible for
943 forwarding them to the various collection processes. The
944 following sections describe the Metrics object and the API
945 associated with it.
946
947
948 Creating The Metrics Object
949 ---------------------------
950
951 The ``xapp`` object can be created directly, or via the xapp
952 framework. When creating directly the xAPP must supply an RMR
953 message for the object to use; when the framework is used to
954 create the object, the message is created as as part of the
955 process. The framework provides three constructors for the
956 metrics instance allowing the xAPP to supply the measurement
957 source, the source and reporter, or to default to using the
958 xAPP name as both the source and reporter (see section
959 *Source and Reporter* for a more detailed description of
960 these). The framework constructors are illustrated in figure
961 17.
962
963
964 ::
965
966     std::unique_ptr<xapp::Metrics> Alloc_metrics( );
967     std::unique_ptr<xapp::Metrics> Alloc_metrics( std::string source );
968     std::unique_ptr<xapp::Metrics> Alloc_metrics( std::string reporter, std::string source );
969
970 Figure 17: The framework constructors for creating an
971 instance of the metrics object.
972
973
974 ::
975
976
977       #include <ricxfcpp/Metrics>
978
979       char* port = (char *) "4560";
980
981       auto x = std::unique_ptr<Xapp>( new Xapp( port ) );
982       auto reading = std::shared_ptr<xapp::Metrics>( x->Alloc_metric( ) );
983
984 Figure 18: Metrics instance creation using the framework.
985
986 Figures 18 illustrates how the framework constructor can be
987 used to create a metrics instance. While it is unlikely that
988 an xAPP will create a metrics instance directly, there are
989 three similar constructors available. These are prototypes
990 are shown in figure 19 and their use is illustrated in figure
991 20.
992
993 ::
994
995      Metrics( std::shared_ptr<xapp::Message> msg );
996      Metrics( std::shared_ptr<xapp::Message> msg, std::string msource );
997      Metrics( std::shared_ptr<xapp::Message> msg, std::string reporter, std::string msource );
998
999 Figure 19: Metrics object constructors.
1000
1001
1002 ::
1003
1004       #include <ricxfcpp/Metrics>
1005
1006       char* port = (char *) "4560";
1007
1008       auto x = std::unique_ptr<Xapp>( new Xapp( port ) );
1009       auto msg = std::shared_ptr<xapp::Message>( x->Alloc_msg( 4096 ) );
1010       auto reading = std::shared_ptr<xapp::Metrics>( new Metrics( msg ) );
1011
1012 Figure 20: Direct creation of a metrics instance.
1013
1014
1015
1016 Adding Values
1017 -------------
1018
1019 Once an instance of the metrics object is created, the xAPP
1020 may push values in preparation to sending the measurement(s)
1021 to the collector. The ``Push_data()`` function is used to
1022 push each key/value pair and is illustrated in figure 21.
1023
1024 ::
1025
1026           reading->Push_data( "normal_count", (double) norm_count );
1027           reading->Push_data( "high_count", (double) hi_count );
1028           reading->Push_data( "excessive_count", (double) ex_count );
1029
1030 Figure 21: Pushing key/value pairs into a metrics instance.
1031
1032
1033
1034 Sending A Measurement Set
1035 -------------------------
1036
1037 After all of the measurement key/value pairs have been added
1038 to the instance, the ``Send()`` function can be invoked to
1039 create the necessary RMR message and send that to the
1040 collection application. Following the send, the key/value
1041 pairs are cleared from the instance and the xAPP is free to
1042 start pushing values into the instance again. The send
1043 function has the following prototype and returns ``true`` on
1044 success and ``false`` if the measurements could not be sent.
1045
1046
1047 Source and Reporter
1048 -------------------
1049
1050 The alarm collector has the understanding that a measurement
1051 might be *sourced* from one piece of equipment, or software
1052 component, but reported by another. For auditing purposes it
1053 makes sense to distinguish these, and as such the metrics
1054 object allows the xAPP to identify the case when the source
1055 and reporter are something other than the xAPP which is
1056 generating the metrics message(s).
1057
1058 The *source* is the component to which the measurement
1059 applies. This could be a network interface card counting
1060 packets, a temperature sensor, or the xAPP itself reporting
1061 xAPP related metrics. The *reporter* is the application that
1062 is reporting the measurement(s) to the collector.
1063
1064 By default, both reporter and source are assumed to be the
1065 xAPP, and the name is automatically determined using the
1066 run-time supplied programme name. Should the xAPP need to
1067 report measurements for more than one source it has the
1068 option to create an instance for every reporter source
1069 combination, or to set the reporter and/or source with the
1070 generation of each measurement set. To facilitate the ability
1071 to change the source and/or the reporter without the need to
1072 create a new metrics instance, two *setter* functions are
1073 provided. The prototypes for these are shown in figure 22.
1074
1075
1076 ::
1077
1078       void Set_source( std::string new_source );
1079       void Set_reporter( std::string new_reporter );
1080
1081 Figure 22: Setter functions allowing the reporter and/or
1082 source to be set after construction.
1083
1084
1085
1086 EXAMPLE PROGRAMMES
1087 ==================
1088
1089 The following sections contain several example programmes
1090 which are written on top of the C++ framework.
1091
1092
1093 RMR Dump xAPP
1094 -------------
1095
1096 The RMR dump application is an example built on top of the
1097 C++ xApp framework to both illustrate the use of the
1098 framework, and to provide a useful diagnostic tool when
1099 testing and troubleshooting xApps.
1100
1101 The RMR dump xApp isn't a traditional xApp inasmuch as its
1102 goal is to listen for message types and to dump information
1103 about the messages received to the TTY much as
1104 ``tcpdump`` does for raw packet traffic. The full source
1105 code, and Makefile, are in the ``examples`` directory of the
1106 C++ framework repo.
1107
1108 When invoked, the RMR dump program is given one or more
1109 message types to listen for. A callback function is
1110 registered for each, and the framework ``Run()`` function is
1111 invoked to drive the process. For each recognised message,
1112 and depending on the verbosity level supplied at program
1113 start, information about the received message(s) is written
1114 to the TTY. If the forwarding option, -f, is given on the
1115 command line, and an appropriate route table is provided,
1116 each received message is forwarded without change. This
1117 allows for the insertion of the RMR dump program into a flow,
1118 however if the ultimate receiver of a message needs to reply
1119 to that message, the reply will not reach the original
1120 sender, so RMR dump is not a complete "middle box"
1121 application.
1122
1123 The following is the code for this xAPP. Several functions,
1124 which provide logic unrelated to the framework, have been
1125 omitted. The full code is in the framework repository.
1126
1127
1128
1129    ::
1130
1131      #include <stdio.h>
1132      #include <unistd.h>
1133      #include <atomic>
1134
1135      #include "ricxfcpp/xapp.hpp"
1136
1137      /*
1138          Information that the callback needs outside
1139          of what is given to it via parms on a call
1140          by the framework.
1141      */
1142      typedef struct {
1143          int        vlevel;             // verbosity level
1144          bool    forward;            // if true, message is forwarded
1145          int        stats_freq;         // header/stats after n messages
1146          std::atomic<long>    pcount; // messages processed
1147          std::atomic<long>    icount; // messages ignored
1148          std::atomic<int>    hdr;    // number of messages before next header
1149      } cb_info_t;
1150
1151      // ----------------------------------------------------------------------
1152
1153      /*
1154          Dump bytes to tty.
1155      */
1156      void dump( unsigned const char* buf, int len ) {
1157          int        i;
1158          int        j;
1159          char    cheater[17];
1160
1161          fprintf( stdout, "<RD> 0000 | " );
1162          j = 0;
1163          for( i = 0; i < len; i++ ) {
1164              cheater[j++] =  isprint( buf[i] ) ? buf[i] : '.';
1165              fprintf( stdout, "%02x ", buf[i] );
1166
1167              if( j == 16 ) {
1168                  cheater[j] = 0;
1169                  fprintf( stdout, " | %s\\n<RD> %04x | ", cheater, i+1 );
1170                  j = 0;
1171              }
1172          }
1173
1174          if( j ) {
1175              i = 16 - (i % 16);
1176              for( ; i > 0; i-- ) {
1177                  fprintf( stdout, "   " );
1178              }
1179              cheater[j] = 0;
1180              fprintf( stdout, " | %s\\n", cheater );
1181          }
1182      }
1183
1184      /*
1185          generate stats when the hdr count reaches 0. Only one active
1186          thread will ever see it be exactly 0, so this is thread safe.
1187      */
1188      void stats( cb_info_t& cbi ) {
1189          int curv;                    // current stat trigger value
1190
1191          curv = cbi.hdr--;
1192
1193          if( curv == 0 ) {                    // stats when we reach 0
1194              fprintf( stdout, "ignored: %ld  processed: %ld\\n",
1195                  cbi.icount.load(), cbi.pcount.load() );
1196              if( cbi.vlevel > 0 ) {
1197                  fprintf( stdout, "\\n     %5s %5s %2s %5s\\n",
1198                      "MTYPE", "SUBID", "ST", "PLLEN" );
1199              }
1200
1201              cbi.hdr = cbi.stats_freq;        // reset must be last
1202          }
1203      }
1204
1205      void cb1( xapp::Message& mbuf, int mtype, int subid, int len,
1206                      xapp::Msg_component payload,  void* data ) {
1207          cb_info_t*    cbi;
1208          long total_count;
1209
1210          if( (cbi = (cb_info_t *) data) == NULL ) {
1211              return;
1212          }
1213
1214          cbi->pcount++;
1215          stats( *cbi );            // gen stats & header if needed
1216
1217          if( cbi->vlevel > 0 ) {
1218              fprintf( stdout, "<RD> %-5d %-5d %02d %-5d \\n",
1219                      mtype, subid, mbuf.Get_state(), len );
1220
1221              if( cbi->vlevel > 1 ) {
1222                  dump(  payload.get(), len > 64 ? 64 : len );
1223              }
1224          }
1225
1226          if( cbi->forward ) {
1227              // forward with no change to len or payload
1228              mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL );
1229          }
1230      }
1231
1232      /*
1233          registered as the default callback; it counts the
1234          messages that we aren't giving details about.
1235      */
1236      void cbd( xapp::Message& mbuf, int mtype, int subid, int len,
1237                      xapp::Msg_component payload,  void* data ) {
1238          cb_info_t*    cbi;
1239
1240          if( (cbi = (cb_info_t *) data) == NULL ) {
1241              return;
1242          }
1243
1244          cbi->icount++;
1245          stats( *cbi );
1246
1247          if( cbi->forward ) {
1248              // forward with no change to len or payload
1249              mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL );
1250          }
1251      }
1252
1253      int main( int argc, char** argv ) {
1254          std::unique_ptr<Xapp> x;
1255          char*    port = (char *) "4560";
1256          int ai = 1;                    // arg processing index
1257          cb_info_t*    cbi;
1258          int        ncb = 0;            // number of callbacks registered
1259          int        mtype;
1260          int        nthreads = 1;
1261
1262          cbi = (cb_info_t *) malloc( sizeof( *cbi ) );
1263          cbi->pcount = 0;
1264          cbi->icount = 0;
1265          cbi->stats_freq = 10;
1266
1267          ai = 1;
1268          // very simple flag parsing (no error/bounds checking)
1269          while( ai < argc ) {
1270              if( argv[ai][0] != '-' )  {        // break on first non-flag
1271                  break;
1272              }
1273
1274              // very simple arg parsing; each must be separate -x -y not -xy.
1275              switch( argv[ai][1] ) {
1276                  case 'f':                    // enable packet forwarding
1277                      cbi->forward = true;
1278                      break;
1279
1280                  case 'p':                    // define port
1281                      port = argv[ai+1];
1282                      ai++;
1283                      break;
1284
1285                  case 's':                        // stats frequency
1286                      cbi->stats_freq = atoi( argv[ai+1] );
1287                      if( cbi->stats_freq < 5 ) {    // enforce sanity
1288                          cbi->stats_freq = 5;
1289                      }
1290                      ai++;
1291                      break;
1292
1293                  case 't':                        // thread count
1294                      nthreads = atoi( argv[ai+1] );
1295                      if( nthreads < 1 ) {
1296                          nthreads = 1;
1297                      }
1298                      ai++;
1299                      break;
1300
1301                  case 'v':            // simple verbose bump
1302                      cbi->vlevel++;
1303                      break;
1304
1305                  case 'V':            // explicit verbose level
1306                      cbi->vlevel = atoi( argv[ai+1] );
1307                      ai++;
1308                      break;
1309
1310                  default:
1311                      fprintf( stderr, "unrecognised option: %s\\n", argv[ai] );
1312                      fprintf( stderr, "usage: %s [-f] [-p port] "
1313                                      "[-s stats-freq]  [-t thread-count] "
1314                                      "[-v | -V n] msg-type1 ... msg-typen\\n",
1315                                      argv[0] );
1316                      fprintf( stderr, "\\tstats frequency is based on # of messages received\\n" );
1317                      fprintf( stderr, "\\tverbose levels (-V) 0 counts only, "
1318                                      "1 message info 2 payload dump\\n" );
1319                      exit( 1 );
1320              }
1321
1322              ai++;
1323          }
1324
1325          cbi->hdr = cbi->stats_freq;
1326          fprintf( stderr, "<RD> listening on port: %s\\n", port );
1327
1328          // create xapp, wait for route table if forwarding
1329          x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
1330
1331          // register callback for each type on the command line
1332          while( ai < argc ) {
1333              mtype = atoi( argv[ai] );
1334              ai++;
1335              fprintf( stderr, "<RD> capturing messages for type %d\\n", mtype );
1336              x->Add_msg_cb( mtype, cb1, cbi );
1337              ncb++;
1338          }
1339
1340          if( ncb < 1 ) {
1341              fprintf( stderr, "<RD> no message types specified on the command line\\n" );
1342              exit( 1 );
1343          }
1344
1345          x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi );        // register default cb
1346
1347          fprintf( stderr, "<RD> starting driver\\n" );
1348          x->Run( nthreads );
1349
1350          // return from run() is not expected, but some compilers might
1351          // compilain if there isn't a return value here.
1352          return 0;
1353      }
1354
1355    Figure 23: Simple callback application.
1356
1357
1358 Callback Receiver
1359 -----------------
1360
1361 This sample programme implements a simple message listener
1362 which registers three callback functions to process two
1363 specific message types and a default callback to handle
1364 unrecognised messages.
1365
1366 When a message of type 1 is received, it will send two
1367 response messages back to the sender. Two messages are sent
1368 in order to illustrate that it is possible to send multiple
1369 responses using the same received message.
1370
1371 The programme illustrates how multiple listening threads can
1372 be used, but the programme is **not** thread safe; to keep
1373 this example as simple as possible, the counters are not
1374 locked when incremented.
1375
1376
1377 Metrics Generation
1378 ------------------
1379
1380 The example also illustrates how a metrics object instance
1381 can be created and used to send appliction metrics to the
1382 collector. In this example the primary callback function will
1383 genereate metrics with the receipt of each 1000th message.
1384
1385
1386    ::
1387
1388      #include <stdio.h>
1389
1390      #include "ricxfcpp/message.hpp"
1391      #include "ricxfcpp/msg_component.hpp"
1392      #include <ricxfcpp/metrics.hpp>
1393      #include "ricxfcpp/xapp.hpp"
1394
1395      // counts; not thread safe
1396      long cb1_count = 0;
1397      long cb2_count = 0;
1398      long cbd_count = 0;
1399
1400      long cb1_lastts = 0;
1401      long cb1_lastc = 0;
1402
1403      /*
1404          Respond with 2 messages for each type 1 received
1405          Send metrics every 1000 messages.
1406      */
1407      void cb1( xapp::Message& mbuf, int mtype, int subid, int len,
1408                  xapp::Msg_component payload,  void* data ) {
1409          long now;
1410          long total_count;
1411
1412          // illustrate that we can use the same buffer for 2 rts calls
1413          mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK1\\n" );
1414          mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\\n" );
1415
1416          cb1_count++;
1417
1418          if( cb1_count % 1000 == 0 && data != NULL ) {   // send metrics every 1000 messages
1419              auto x = (Xapp *) data;
1420              auto msgm = std::shared_ptr<xapp::Message>( x->Alloc_msg( 4096 ) );
1421
1422              auto m = std::unique_ptr<xapp::Metrics>( new xapp::Metrics( msgm ) );
1423              m->Push_data( "tst_cb1", (double) cb1_count );
1424              m->Push_data( "tst_cb2", (double) cb2_count );
1425              m->Send();
1426          }
1427      }
1428
1429      // just count messages
1430      void cb2( xapp::Message& mbuf, int mtype, int subid, int len,
1431                  xapp::Msg_component payload,  void* data ) {
1432          cb2_count++;
1433      }
1434
1435      // default to count all unrecognised messages
1436      void cbd( xapp::Message& mbuf, int mtype, int subid, int len,
1437                  xapp::Msg_component payload,  void* data ) {
1438          cbd_count++;
1439      }
1440
1441      int main( int argc, char** argv ) {
1442          Xapp* x;
1443          char*    port = (char *) "4560";
1444          int ai = 1;                            // arg processing index
1445          int nthreads = 1;
1446
1447          // very simple flag processing (no bounds/error checking)
1448          while( ai < argc ) {
1449              if( argv[ai][0] != '-' )  {
1450                  break;
1451              }
1452
1453              switch( argv[ai][1] ) {            // we only support -x so -xy must be -x -y
1454                  case 'p':
1455                      port = argv[ai+1];
1456                      ai++;
1457                      break;
1458
1459                  case 't':
1460                      nthreads = atoi( argv[ai+1] );
1461                      ai++;
1462                      break;
1463              }
1464
1465              ai++;
1466          }
1467
1468          fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
1469          fprintf( stderr, "<XAPP> starting %d threads\\n", nthreads );
1470
1471          x = new Xapp( port, true );
1472          x->Add_msg_cb( 1, cb1, x );        // register callbacks
1473          x->Add_msg_cb( 2, cb2, NULL );
1474          x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL );
1475
1476          x->Run( nthreads );                // let framework drive
1477          // control should not return
1478      }
1479
1480    Figure 24: Simple callback application.
1481
1482
1483
1484 Looping Sender
1485 --------------
1486
1487 This is another very simple application which demonstrates
1488 how an application can control its own listen loop while
1489 sending messages. As with the other examples, some error
1490 checking is skipped, and short cuts have been made in order
1491 to keep the example small and to the point.
1492
1493
1494    ::
1495
1496
1497      #include <stdio.h>
1498      #include <string.h>
1499      #include <unistd.h>
1500
1501      #include <iostream>
1502      #include <memory>
1503
1504      #include "ricxfcpp/xapp.hpp"
1505
1506      extern int main( int argc, char** argv ) {
1507          std::unique_ptr<Xapp> xfw;
1508          std::unique_ptr<xapp::Message> msg;
1509          xapp::Msg_component payload;                // special type of unique pointer to the payload
1510
1511          int    sz;
1512          int len;
1513          int i;
1514          int ai;
1515          int response_to = 0;                // max timeout wating for a response
1516          char*    port = (char *) "4555";
1517          int    mtype = 0;
1518          int rmtype;                            // received message type
1519          int delay = 1000000;                // mu-sec delay; default 1s
1520
1521
1522          // very simple flag processing (no bounds/error checking)
1523          while( ai < argc ) {
1524              if( argv[ai][0] != '-' )  {
1525                  break;
1526              }
1527
1528              // we only support -x so -xy must be -x -y
1529              switch( argv[ai][1] ) {
1530                  // delay between messages (mu-sec)
1531                  case 'd':
1532                      delay = atoi( argv[ai+1] );
1533                      ai++;
1534                      break;
1535
1536                  case 'p':
1537                      port = argv[ai+1];
1538                      ai++;
1539                      break;
1540
1541                  // timeout in seconds; we need to convert to ms for rmr calls
1542                  case 't':
1543                      response_to = atoi( argv[ai+1] ) * 1000;
1544                      ai++;
1545                      break;
1546              }
1547              ai++;
1548          }
1549
1550          fprintf( stderr, "<XAPP> response timeout set to: %d\\n", response_to );
1551          fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
1552
1553          // get an instance and wait for a route table to be loaded
1554          xfw = std::unique_ptr<Xapp>( new Xapp( port, true ) );
1555          msg = xfw->Alloc_msg( 2048 );
1556
1557          for( i = 0; i < 100; i++ ) {
1558              mtype++;
1559              if( mtype > 10 ) {
1560                  mtype = 0;
1561              }
1562
1563              // we'll reuse a received message; get max size
1564              sz = msg->Get_available_size();
1565
1566              // direct access to payload; add something silly
1567              payload = msg->Get_payload();
1568              len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i );
1569
1570              // payload updated in place, prevent copy by passing nil
1571              if ( ! msg->Send_msg( mtype, xapp::Message::NO_SUBID,  len, NULL )) {
1572                  fprintf( stderr, "<SNDR> send failed: %d\\n", i );
1573              }
1574
1575              // receive anything that might come back
1576              msg = xfw->Receive( response_to );
1577              if( msg != NULL ) {
1578                  rmtype = msg->Get_mtype();
1579                  payload = msg->Get_payload();
1580                  fprintf( stderr, "got: mtype=%d payload=(%s)\\n",
1581                      rmtype, (char *) payload.get() );
1582              } else {
1583                  msg = xfw->Alloc_msg( 2048 );
1584              }
1585
1586              if( delay > 0 ) {
1587                  usleep( delay );
1588              }
1589          }
1590      }
1591
1592    Figure 25: Simple looping sender application.
1593
1594
1595
1596 Alarm Example
1597 -------------
1598
1599    This is an extension of a previous example which sends an
1600    alarm during initialisation and clears the alarm as soon
1601    as messages are being received. It is unknown if this is
1602    the type of alarm that is expected at the collector, but
1603    illustrates how an alarm is allocated, raised and cleared.
1604
1605
1606       ::
1607
1608
1609         #include <stdio.h>
1610         #include <string.h>
1611         #include <unistd.h>
1612
1613         #include <iostream>
1614         #include <memory>
1615
1616         #include "ricxfcpp/xapp.hpp"
1617         #include "ricxfcpp/alarm.hpp"
1618
1619         extern int main( int argc, char** argv ) {
1620             std::unique_ptr<Xapp> xfw;
1621             std::unique_ptr<xapp::Message> msg;
1622             xapp::Msg_component payload;                // special type of unique pointer to the payload
1623             std::unique_ptr<xapp::Alarm>    alarm;
1624
1625             bool received = false;                // false until we've received a message
1626             int    sz;
1627             int len;
1628             int i;
1629             int ai = 1;
1630             int response_to = 0;                // max timeout wating for a response
1631             char*    port = (char *) "4555";
1632             int    mtype = 0;
1633             int rmtype;                            // received message type
1634             int delay = 1000000;                // mu-sec delay; default 1s
1635
1636
1637             // very simple flag processing (no bounds/error checking)
1638             while( ai < argc ) {
1639                 if( argv[ai][0] != '-' )  {
1640                     break;
1641                 }
1642
1643                 // we only support -x so -xy must be -x -y
1644                 switch( argv[ai][1] ) {
1645                     // delay between messages (mu-sec)
1646                     case 'd':
1647                         delay = atoi( argv[ai+1] );
1648                         ai++;
1649                         break;
1650
1651                     case 'p':
1652                         port = argv[ai+1];
1653                         ai++;
1654                         break;
1655
1656                     // timeout in seconds; we need to convert to ms for rmr calls
1657                     case 't':
1658                         response_to = atoi( argv[ai+1] ) * 1000;
1659                         ai++;
1660                         break;
1661                 }
1662                 ai++;
1663             }
1664
1665             fprintf( stderr, "<XAPP> response timeout set to: %d\\n", response_to );
1666             fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
1667
1668             // get an instance and wait for a route table to be loaded
1669             xfw = std::unique_ptr<Xapp>( new Xapp( port, true ) );
1670             msg = xfw->Alloc_msg( 2048 );
1671
1672
1673             // raise an unavilable alarm which we'll clear on the first recevied message
1674             alarm =  xfw->Alloc_alarm( "meid-1234"  );
1675             alarm->Raise( xapp::Alarm::SEV_MINOR, 13, "unavailable", "no data recevied" );
1676
1677             for( i = 0; i < 100; i++ ) {
1678                 mtype++;
1679                 if( mtype > 10 ) {
1680                     mtype = 0;
1681                 }
1682
1683                 // we'll reuse a received message; get max size
1684                 sz = msg->Get_available_size();
1685
1686                 // direct access to payload; add something silly
1687                 payload = msg->Get_payload();
1688                 len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i );
1689
1690                 // payload updated in place, prevent copy by passing nil
1691                 if ( ! msg->Send_msg( mtype, xapp::Message::NO_SUBID,  len, NULL )) {
1692                     fprintf( stderr, "<SNDR> send failed: %d\\n", i );
1693                 }
1694
1695                 // receive anything that might come back
1696                 msg = xfw->Receive( response_to );
1697                 if( msg != NULL ) {
1698                     if( ! received ) {
1699                         alarm->Clear( xapp::Alarm::SEV_MINOR, 13, "messages flowing", "" );                // clear the alarm on first received message
1700                         received = true;
1701                     }
1702
1703                     rmtype = msg->Get_mtype();
1704                     payload = msg->Get_payload();
1705                     fprintf( stderr, "got: mtype=%d payload=(%s)\\n",
1706                         rmtype, (char *) payload.get() );
1707                 } else {
1708                     msg = xfw->Alloc_msg( 2048 );
1709                 }
1710
1711                 if( delay > 0 ) {
1712                     usleep( delay );
1713                 }
1714             }
1715         }
1716
1717       Figure 26: Simple looping sender application with alarm
1718       generation.
1719