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