Add json support
[ric-plt/xapp-frame-cpp.git] / docs / user-guide.rst
1 .. This work is licensed under a Creative Commons Attribution 4.0 International License.
2 .. SPDX-License-Identifier: CC-BY-4.0
3 ..
4 .. CAUTION: this document is generated from source in doc/src/*
5 .. To make changes edit the source and recompile the document.
6 .. Do NOT make changes directly to .rst or .md files.
7
8
9 ============================================================================================
10 User's Guide
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       port
47          A C string (pointer to char) which defines the port that
48          RMR will open to listen for connections.
49
50       wait
51          A Boolean value which indicates whether or not the
52          initialization process should wait for the arrival of a
53          valid route table before completing. When true is
54          supplied, the initialization will not complete until RMR
55          has received a valid route table (or one is located via
56          the RMR_SEED_RT environment variable).
57
58       The following code sample illustrates the simplicity of
59       creating the instance of the xApp object.
60
61
62       ::
63
64             #include <memory>
65             #include <ricxfcpp/xapp.hpp>
66             int main( ) {
67                 std::unique_ptr<Xapp> xapp;
68                 char* listen_port = (char *) "4560";    //RMR listen port
69                 bool  wait4table = true;            // wait for a route table
70
71                 xapp = std::unique_ptr<Xapp>(
72                       new Xapp( listen_port, wait4table ) );
73             }
74
75       Figure 1: Creating an xAPP instance.
76
77       From a compilation perspective, the following is the simple
78       compiler invocation string needed to compile and link the
79       above program (assuming that the sample code exists in a file
80       called man_ex1.cpp.
81
82
83       ::
84
85            g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread
86
87
88       The above program, while complete and capable of being
89       compiled, does nothing useful. When invoked, RMR will be
90       initialized and will begin listening for a route table;
91       blocking the return to the main program until one is
92       received. When a valid route table arrives, initialization
93       will complete and the program will exit as there is no code
94       following the instruction to create the object.
95
96
97 LISTENING FOR MESSAGES
98 ======================
99
100       The program in the previous example can be extended with just
101       a few lines of code to enable it to receive and process
102       messages. The application needs to register a callback
103       function for each message type which it desires to process.
104
105       Once registered, each time a message is received the
106       registered callback for the message type will be invoked by
107       the framework.
108
109
110 Callback Signature
111 ------------------
112
113       As with most callback related systems, a callback must have a
114       well known function signature which generally passes event
115       related information and a "user" data pointer which was
116       registered with the function. The following is the prototype
117       which callback functions must be defined with:
118
119
120       ::
121
122             void cb_name( Message& m, int mtype, int subid,
123                   int payload_len, Msg_component payload,
124                   void* usr_data );
125
126       Figure 2: Callback function signature
127
128       The parameters passed to the callback function are as
129       follows: &multi_space
130
131       m
132          A reference to the Message that was received.
133
134       mtype
135          The message type (allows for disambiguation if the
136          callback is registered for multiple message types).
137
138       subid
139          The subscription ID from the message.
140
141       payload len
142          The number of bytes which the sender has placed into the
143          payload.
144
145       payload
146          A direct reference (smart pointer) to the payload. (The
147          smart pointer is wrapped in a special class in order to
148          provide a custom destruction function without burdening
149          the xApp developer with that knowledge.)
150
151       user data
152          A pointer to user data. This is the pointer that was
153          provided when the function was registered.
154
155       To illustrate the use of a callback function, the previous
156       code example has been extended to add the function, register
157       it for message types 1000 and 1001, and to invoke the Run()
158       function in the framework (explained in the next section).
159
160       ::
161
162             #include <memory>
163             #include <ricxfcpp/xapp.hpp>
164             long m1000_count = 0;    // message counters, one for each type
165             long m1001_count = 0;
166
167             // callback function that will increase the appropriate counter
168             void cbf( Message& mbuf, int mtype, int subid, int len,
169                         Msg_component payload,  void* data ) {
170                 long* counter;
171
172                 if( (counter = (long *) data) != NULL ) {
173                     (*counter)++;
174                 }
175             }
176
177             int main( ) {
178                 std::unique_ptr<Xapp> xapp;
179                 char* listen_port = (char *) "4560";
180                 bool  wait4table = false;
181
182                 xapp = std::unique_ptr<Xapp>(
183                       new Xapp( listen_port, wait4table ) );
184
185                 // register the same callback function for both msg types
186                 xapp->Add_msg_cb( 1000, cbf, (void *) &m1000_count );
187                 xapp->Add_msg_cb( 1001, cbf, (void *) &m1001_count );
188
189                 xapp->Run( 1 );        // start the callback driver
190             }
191
192       Figure 3: Callback function example.
193
194       As before, the program does nothing useful, but now it will
195       execute and receive messages. For this example, the same
196       function can be used to increment the appropriate counter
197       simply by providing a pointer to the counter as the user data
198       when the callback function is registered. In addition, a
199       subtle change from the previous example has been to set the
200       wait for table flag to false.
201
202       For an xApp that is a receive only application (never sends)
203       it is not necessary to wait for RMR to receive a table from
204       the Route Manager.
205
206
207 Registering A Default Callback
208 ------------------------------
209
210       The xApp may also register a default callback function such
211       that the function will be invoked for any message that does
212       not have a registered callback. If the xAPP does not register
213       a default callback, any message which cannot be mapped to a
214       known callback function is silently dropped. A default
215       callback is registered by providing a *generic* message type
216       of xapp->DEFAULT_CALLBACK on an Add_msg_cb call.
217
218
219 The Framework Callback Driver
220 -----------------------------
221
222       The Run() function within the Xapp object is invoked to start
223       the callback driver, and the xApp should not expect the
224       function to return under most circumstances. The only
225       parameter that the Run() function expects is the number of
226       threads to start. For each thread requested, the framework
227       will start a listener thread which will allow received
228       messages to be processed in parallel. If supplying a value
229       greater than one, the xApp must ensure that the callback
230       functions are thread safe as it is very likely that the same
231       callback function will be invoked concurrently from multiple
232       threads.
233
234
235 SENDING MESSAGES
236 ================
237
238       It is very likely that most xApps will need to send messages
239       and will not operate in "receive only" mode. Sending the
240       message is a function of the message object itself and can
241       take one of two forms:
242
243
244       + Replying to the sender of a received message
245
246       + Sending a message (routed based on the message type and subscription ID)
247
248
249       When replying to the sender, the message type and
250       subscription ID are not used to determine the destination of
251       the message; RMR ensures that the message is sent back to the
252       originating xApp. The xApp may still need to change the
253       message type and/or the subscription ID in the message prior
254       to using the reply function.
255
256       To provide for both situations, two reply functions are
257       supported by the Message object as illustrated with the
258       following prototypes.
259
260
261       ::
262
263            bool Send_response(  int mtype, int subid, int response_len,
264                 std:shared_ptr<unsigned char> response );
265
266            bool Send_response(  int response_len, std::shared_ptr<unsigned char> response );
267
268       Figure 4: Reply function prototypes.
269
270       In the first prototype the xApp must supply the new message
271       type and subscription ID values, where the second function
272       uses the values which are currently set in the message.
273       Further, the new payload contents, and length, are supplied
274       to both functions; the framework ensures that the message is
275       large enough to accommodate the payload, reallocating it if
276       necessary, and copies the response into the message payload
277       prior to sending. Should the xApp need to change either the
278       message type, or the subscription ID, but not both, the
279       NO_CHANGE constant can be used as illustrated below.
280
281
282       ::
283
284             msg->Send_response( Message::NO_CHANGE, Message::NO_SUBID,
285                 pl_length, (unsigned char *) payload );
286
287       Figure 5: Send response prototype.
288
289       In addition to the two function prototypes for
290       Send_response() there are two additional prototypes which
291       allow the new payload to be supplied as a shared smart
292       pointer. The other parameters to these functions are
293       identical to those illustrated above, and thus are not
294       presented here.
295
296       The Send_msg() set of functions supported by the Message
297       object are identical to the Send_response() functions and are
298       shown below.
299
300
301       ::
302
303             bool Send_msg( int mtype, int subid, int payload_len,
304                 std::shared_ptr<unsigned char> payload );
305
306             bool Send_msg( int mtype, int subid, int payload_len,
307                 unsigned char* payload );
308
309             bool Send_msg( int payload_len,
310                 std::shared_ptr<unsigned char> payload );
311
312             bool Send_msg( int payload_len, unsigned char* payload );
313
314       Figure 6: Send function prototypes.
315
316       Each send function accepts the message, copies in the payload
317       provided, sets the message type and subscription ID (if
318       provided), and then causes the message to be sent. The only
319       difference between the Send_msg() and Send_response()
320       functions is that the destination of the message is selected
321       based on the mapping of the message type and subscription ID
322       using the current routing table known to RMR.
323
324
325 Direct Payload Manipulation
326 ---------------------------
327
328       For some applications, it might be more efficient to
329       manipulate the payload portion of an Xapp Message in place,
330       rather than creating it and relying on a buffer copy when the
331       message is finally sent. To achieve this, the xApp must
332       either use the smart pointer to the payload passed to the
333       callback function, or retrieve one from the message using
334       Get_payload() when working with a message outside of a
335       callback function. Once the smart pointer is obtained, the
336       pointer's get() function can be used to directly reference
337       the payload (unsigned char) bytes.
338
339       When working directly with the payload, the xApp must take
340       care not to write more than the actual payload size which can
341       be extracted from the Message object using the
342       Get_available_size() function.
343
344       When sending a message where the payload has been directly
345       altered, and no extra buffer copy is needed, a NULL pointer
346       should be passed to the Message send function. The following
347       illustrates how the payload can be directly manipulated and
348       returned to the sender (for simplicity, there is no error
349       handling if the payload size of the received message isn't
350       large enough for the response string, the response is just
351       not sent).
352
353
354       ::
355
356             Msg_component payload;  // smart reference
357             int pl_size;            // max size of payload
358
359             payload = msg->Get_payload();
360             pl_size = msg->Get_available_size();
361             if( snprintf( (char *) payload.get(), pl_size,
362                 "Msg Received\\n" ) < pl_size ) {
363               msg->Send_response( M_TYPE, SID, strlen( raw_pl ), NULL );
364             }
365
366       Figure 7: Send message without buffer copy.
367
368
369
370 Sending Multiple Responses
371 --------------------------
372
373       It is likely that the xApp will wish to send multiple
374       responses back to the process that sent a message that
375       triggered the callback. The callback function may invoke the
376       Send_response() function multiple times before returning.
377
378       After each call, the Message retains the necessary
379       information to allow for a subsequent invocation to send more
380       data. It should be noted though, that after the first call to
381       {Send_response() the original payload will be lost; if
382       necessary, the xApp must make a copy of the payload before
383       the first response call is made.
384
385
386 Message Allocation
387 ------------------
388
389       Not all xApps will be "responders," meaning that some xApps
390       will need to send one or more messages before they can expect
391       to receive any messages back. To accomplish this, the xApp
392       must first allocate a message buffer, optionally initialising
393       the payload, and then using the message's Send_msg() function
394       to send a message out. The framework's Alloc_msg() function
395       can be used to create a Message object with a desired payload
396       size.
397
398
399 FRAMEWORK PROVIDED CALLBACKS
400 ============================
401
402       The framework itself may provide message handling via the
403       driver such that the xApp might not need to implement some
404       message processing functionality. Initially, the C++
405       framework will provide a default callback function to handle
406       the RMR based health check messages. This callback function
407       will assume that if the message was received, and the
408       callback invoked, that all is well and will reply with an OK
409       state. If the xApp should need to override this simplistic
410       response, all it needs to do is to register its own callback
411       function for the health check message type.
412
413
414 EXAMPLE PROGRAMMES
415 ==================
416
417       The following sections contain several example programmes
418       which are written on top of the C++ framework.
419
420
421 RMR Dump xAPP
422 -------------
423
424       The RMR dump application is an example built on top of the
425       C++ xApp framework to both illustrate the use of the
426       framework, and to provide a useful diagnostic tool when
427       testing and troubleshooting xApps.
428
429       The RMR dump xApp isn't a traditional xApp inasmuch as its
430       goal is to listen for message types and to dump information
431       about the messages received to the TTY much as tcpdump does
432       for raw packet traffic. The full source code, and Makefile,
433       are in the examples directory of the C++ framework repo.
434
435       When invoked, the RMR dump program is given one or more
436       message types to listen for. A callback function is
437       registered for each, and the framework Run() function is
438       invoked to drive the process. For each recognised message,
439       and depending on the verbosity level supplied at program
440       start, information about the received message(s) is written
441       to the TTY. If the forwarding option, -f, is given on the
442       command line, and an appropriate route table is provided,
443       each received message is forwarded without change. This
444       allows for the insertion of the RMR dump program into a flow,
445       however if the ultimate receiver of a message needs to reply
446       to that message, the reply will not reach the original
447       sender, so RMR dump is not a complete "middle box"
448       application.
449
450       The following is the code for this xAPP. Several functions,
451       which provide logic unrelated to the framework, have been
452       omitted. The full code is in the framework repository.
453
454
455
456       ::
457
458         #include <stdio.h>
459         #include <unistd.h>
460         #include <atomic>
461
462         #include "ricxfcpp/xapp.hpp"
463
464         /*
465             Information that the callback needs outside
466             of what is given to it via parms on a call
467             by the framework.
468         */
469         typedef struct {
470             int        vlevel;             // verbosity level
471             bool    forward;            // if true, message is forwarded
472             int        stats_freq;         // header/stats after n messages
473             std::atomic<long>    pcount; // messages processed
474             std::atomic<long>    icount; // messages ignored
475             std::atomic<int>    hdr;    // number of messages before next header
476         } cb_info_t;
477
478         // ----------------------------------------------------------------------
479
480         /*
481             Dump bytes to tty.
482         */
483         void dump( unsigned const char* buf, int len ) {
484             int        i;
485             int        j;
486             char    cheater[17];
487
488             fprintf( stdout, "<RD> 0000 | " );
489             j = 0;
490             for( i = 0; i < len; i++ ) {
491                 cheater[j++] =  isprint( buf[i] ) ? buf[i] : '.';
492                 fprintf( stdout, "%02x ", buf[i] );
493
494                 if( j == 16 ) {
495                     cheater[j] = 0;
496                     fprintf( stdout, " | %s\\n<RD> %04x | ", cheater, i+1 );
497                     j = 0;
498                 }
499             }
500
501             if( j ) {
502                 i = 16 - (i % 16);
503                 for( ; i > 0; i-- ) {
504                     fprintf( stdout, "   " );
505                 }
506                 cheater[j] = 0;
507                 fprintf( stdout, " | %s\\n", cheater );
508             }
509         }
510
511         /*
512             generate stats when the hdr count reaches 0. Only one active
513             thread will ever see it be exactly 0, so this is thread safe.
514         */
515         void stats( cb_info_t& cbi ) {
516             int curv;                    // current stat trigger value
517
518             curv = cbi.hdr--;
519
520             if( curv == 0 ) {                    // stats when we reach 0
521                 fprintf( stdout, "ignored: %ld  processed: %ld\\n",
522                     cbi.icount.load(), cbi.pcount.load() );
523                 if( cbi.vlevel > 0 ) {
524                     fprintf( stdout, "\\n     %5s %5s %2s %5s\\n",
525                         "MTYPE", "SUBID", "ST", "PLLEN" );
526                 }
527
528                 cbi.hdr = cbi.stats_freq;        // reset must be last
529             }
530         }
531
532         void cb1( Message& mbuf, int mtype, int subid, int len,
533                         Msg_component payload,  void* data ) {
534             cb_info_t*    cbi;
535             long total_count;
536
537             if( (cbi = (cb_info_t *) data) == NULL ) {
538                 return;
539             }
540
541             cbi->pcount++;
542             stats( *cbi );            // gen stats & header if needed
543
544             if( cbi->vlevel > 0 ) {
545                 fprintf( stdout, "<RD> %-5d %-5d %02d %-5d \\n",
546                         mtype, subid, mbuf.Get_state(), len );
547
548                 if( cbi->vlevel > 1 ) {
549                     dump(  payload.get(), len > 64 ? 64 : len );
550                 }
551             }
552
553             if( cbi->forward ) {
554                 // forward with no change to len or payload
555                 mbuf.Send_msg( Message::NO_CHANGE, NULL );
556             }
557         }
558
559         /*
560             registered as the default callback; it counts the
561             messages that we aren't giving details about.
562         */
563         void cbd( Message& mbuf, int mtype, int subid, int len,
564                         Msg_component payload,  void* data ) {
565             cb_info_t*    cbi;
566
567             if( (cbi = (cb_info_t *) data) == NULL ) {
568                 return;
569             }
570
571             cbi->icount++;
572             stats( *cbi );
573
574             if( cbi->forward ) {
575                 // forward with no change to len or payload
576                 mbuf.Send_msg( Message::NO_CHANGE, NULL );
577             }
578         }
579
580         int main( int argc, char** argv ) {
581             std::unique_ptr<Xapp> x;
582             char*    port = (char *) "4560";
583             int ai = 1;                    // arg processing index
584             cb_info_t*    cbi;
585             int        ncb = 0;            // number of callbacks registered
586             int        mtype;
587             int        nthreads = 1;
588
589             cbi = (cb_info_t *) malloc( sizeof( *cbi ) );
590             cbi->pcount = 0;
591             cbi->icount = 0;
592             cbi->stats_freq = 10;
593
594             ai = 1;
595             // very simple flag parsing (no error/bounds checking)
596             while( ai < argc ) {
597                 if( argv[ai][0] != '-' )  {        // break on first non-flag
598                     break;
599                 }
600
601                 // very simple arg parsing; each must be separate -x -y not -xy.
602                 switch( argv[ai][1] ) {
603                     case 'f':                    // enable packet forwarding
604                         cbi->forward = true;
605                         break;
606
607                     case 'p':                     // define port
608                         port = argv[ai+1];
609                         ai++;
610                         break;
611
612                     case 's':                        // stats frequency
613                         cbi->stats_freq = atoi( argv[ai+1] );
614                         if( cbi->stats_freq < 5 ) {    // enforce sanity
615                             cbi->stats_freq = 5;
616                         }
617                         ai++;
618                         break;
619
620                     case 't':                        // thread count
621                         nthreads = atoi( argv[ai+1] );
622                         if( nthreads < 1 ) {
623                             nthreads = 1;
624                         }
625                         ai++;
626                         break;
627
628                     case 'v':            // simple verbose bump
629                         cbi->vlevel++;
630                         break;
631
632                     case 'V':            // explicit verbose level
633                         cbi->vlevel = atoi( argv[ai+1] );
634                         ai++;
635                         break;
636
637                     default:
638                         fprintf( stderr, "unrecognised option: %s\\n", argv[ai] );
639                         fprintf( stderr, "usage: %s [-f] [-p port] "
640                                         "[-s stats-freq]  [-t thread-count] "
641                                         "[-v | -V n] msg-type1 ... msg-typen\\n",
642                                         argv[0] );
643                         fprintf( stderr, "\\tstats frequency is based on # of messages received\\n" );
644                         fprintf( stderr, "\\tverbose levels (-V) 0 counts only, "
645                                         "1 message info 2 payload dump\\n" );
646                         exit( 1 );
647                 }
648
649                 ai++;
650             }
651
652             cbi->hdr = cbi->stats_freq;
653             fprintf( stderr, "<RD> listening on port: %s\\n", port );
654
655             // create xapp, wait for route table if forwarding
656             x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
657
658             // register callback for each type on the command line
659             while( ai < argc ) {
660                 mtype = atoi( argv[ai] );
661                 ai++;
662                 fprintf( stderr, "<RD> capturing messages for type %d\\n", mtype );
663                 x->Add_msg_cb( mtype, cb1, cbi );
664                 ncb++;
665             }
666
667             if( ncb < 1 ) {
668                 fprintf( stderr, "<RD> no message types specified on the command line\\n" );
669                 exit( 1 );
670             }
671
672             x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi );        // register default cb
673
674             fprintf( stderr, "<RD> starting driver\\n" );
675             x->Run( nthreads );
676
677             // return from run() is not expected, but some compilers might
678             // compilain if there isn't a return value here.
679             return 0;
680         }
681
682       Figure 8: Simple callback application.
683
684
685 Callback Receiver
686 -----------------
687
688       This sample programme implements a simple message listener
689       which registers three callback functions to process two
690       specific message types and a default callback to handle
691       unrecognised messages.
692
693       When a message of type 1 is received, it will send two
694       response messages back to the sender. Two messages are sent
695       in order to illustrate that it is possible to send multiple
696       responses using the same received message.
697
698       The programme illustrates how multiple listening threads can
699       be used, but the programme is **not** thread safe; to keep
700       this example as simple as possible, the counters are not
701       locked when incremented.
702
703
704       ::
705
706         #include <stdio.h>
707
708         #include "ricxfcpp/message.hpp"
709         #include "ricxfcpp/msg_component.hpp"
710         #include "ricxfcpp/xapp.hpp"
711
712         // counts; not thread safe
713         long cb1_count = 0;
714         long cb2_count = 0;
715         long cbd_count = 0;
716
717         long cb1_lastts = 0;
718         long cb1_lastc = 0;
719
720         // respond with 2 messages for each type 1 received
721         void cb1( Message& mbuf, int mtype, int subid, int len,
722                     Msg_component payload,  void* data ) {
723             long now;
724             long total_count;
725
726             // illustrate that we can use the same buffer for 2 rts calls
727             mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK1\\n" );
728             mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\\n" );
729
730             cb1_count++;
731         }
732
733         // just count messages
734         void cb2( Message& mbuf, int mtype, int subid, int len,
735                     Msg_component payload,  void* data ) {
736             cb2_count++;
737         }
738
739         // default to count all unrecognised messages
740         void cbd( Message& mbuf, int mtype, int subid, int len,
741                     Msg_component payload,  void* data ) {
742             cbd_count++;
743         }
744
745         int main( int argc, char** argv ) {
746             Xapp* x;
747             char*    port = (char *) "4560";
748             int ai = 1;                            // arg processing index
749             int nthreads = 1;
750
751             // very simple flag processing (no bounds/error checking)
752             while( ai < argc ) {
753                 if( argv[ai][0] != '-' )  {
754                     break;
755                 }
756
757                 switch( argv[ai][1] ) {            // we only support -x so -xy must be -x -y
758                     case 'p':
759                         port = argv[ai+1];
760                         ai++;
761                         break;
762
763                     case 't':
764                         nthreads = atoi( argv[ai+1] );
765                         ai++;
766                         break;
767                 }
768
769                 ai++;
770             }
771
772             fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
773             fprintf( stderr, "<XAPP> starting %d threads\\n", nthreads );
774
775             x = new Xapp( port, true );
776             x->Add_msg_cb( 1, cb1, NULL );                // register callbacks
777             x->Add_msg_cb( 2, cb2, NULL );
778             x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL );
779
780             x->Run( nthreads );                // let framework drive
781             // control should not return
782         }
783
784       Figure 9: Simple callback application.
785
786
787
788 Looping Sender
789 --------------
790
791       This is another very simple application which demonstrates
792       how an application can control its own listen loop while
793       sending messages. As with the other examples, some error
794       checking is skipped, and short cuts have been made in order
795       to keep the example small and to the point.
796
797
798       ::
799
800
801         #include <stdio.h>
802         #include <string.h>
803         #include <unistd.h>
804
805         #include <iostream>
806         #include <memory>
807
808         #include "ricxfcpp/xapp.hpp"
809
810         extern int main( int argc, char** argv ) {
811             std::unique_ptr<Xapp> xfw;
812             std::unique_ptr<Message> msg;
813             Msg_component payload;                // special type of unique pointer to the payload
814
815             int    sz;
816             int len;
817             int i;
818             int ai;
819             int response_to = 0;                // max timeout wating for a response
820             char*    port = (char *) "4555";
821             int    mtype = 0;
822             int rmtype;                            // received message type
823             int delay = 1000000;                // mu-sec delay; default 1s
824
825
826             // very simple flag processing (no bounds/error checking)
827             while( ai < argc ) {
828                 if( argv[ai][0] != '-' )  {
829                     break;
830                 }
831
832                 // we only support -x so -xy must be -x -y
833                 switch( argv[ai][1] ) {
834                     // delay between messages (mu-sec)
835                     case 'd':
836                         delay = atoi( argv[ai+1] );
837                         ai++;
838                         break;
839
840                     case 'p':
841                         port = argv[ai+1];
842                         ai++;
843                         break;
844
845                     // timeout in seconds; we need to convert to ms for rmr calls
846                     case 't':
847                         response_to = atoi( argv[ai+1] ) * 1000;
848                         ai++;
849                         break;
850                 }
851                 ai++;
852             }
853
854             fprintf( stderr, "<XAPP> response timeout set to: %d\\n", response_to );
855             fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
856
857             // get an instance and wait for a route table to be loaded
858             xfw = std::unique_ptr<Xapp>( new Xapp( port, true ) );
859             msg = xfw->Alloc_msg( 2048 );
860
861             for( i = 0; i < 100; i++ ) {
862                 mtype++;
863                 if( mtype > 10 ) {
864                     mtype = 0;
865                 }
866
867                 // we'll reuse a received message; get max size
868                 sz = msg->Get_available_size();
869
870                 // direct access to payload; add something silly
871                 payload = msg->Get_payload();
872                 len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i );
873
874                 // payload updated in place, prevent copy by passing nil
875                 if ( ! msg->Send_msg( mtype, Message::NO_SUBID,  len, NULL )) {
876                     fprintf( stderr, "<SNDR> send failed: %d\\n", i );
877                 }
878
879                 // receive anything that might come back
880                 msg = xfw->Receive( response_to );
881                 if( msg != NULL ) {
882                     rmtype = msg->Get_mtype();
883                     payload = msg->Get_payload();
884                     fprintf( stderr, "got: mtype=%d payload=(%s)\\n",
885                         rmtype, (char *) payload.get() );
886                 } else {
887                     msg = xfw->Alloc_msg( 2048 );
888                 }
889
890                 if( delay > 0 ) {
891                     usleep( delay );
892                 }
893             }
894         }
895
896       Figure 10: Simple looping sender application.
897