Unit test improvements, add example programme
[ric-plt/xapp-frame-cpp.git] / examples / rmr_dump.cpp
diff --git a/examples/rmr_dump.cpp b/examples/rmr_dump.cpp
new file mode 100644 (file)
index 0000000..5cced21
--- /dev/null
@@ -0,0 +1,259 @@
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+*/
+
+/*
+       Mnemonic:       rmr_dump.cpp
+       Abstract:       This is a sample xapp which will behave somewhat like tcpdump
+                               as it receives rmr messages.  The list of message types which should
+                               be processed are given on the command line, following any flag
+                               options (e.g. -p port).  Any of those messages received are reported
+                               on by the application.  The verbosity level may be used to increase
+                               the amount of detail given for the tracked messages.
+
+                               This writes to the TTY which is slow, so do not expect it to be able 
+                               to process and report on a high rate of messages. Also, forwarded
+                               messages will reach the intended target, however if the target 
+                               attempts to send a response the response will come back to THIS
+                               application, and not the message origination; this cannot be a bit
+                               of middleware in it's current form.
+
+       Date:           25 March 2020
+       Author:         E. Scott Daniels
+
+*/
+#include <stdio.h>
+#include <unistd.h>
+#include <atomic>
+
+#include "ricxfcpp/xapp.hpp"
+
+/*
+       Information that the callback needs outside
+       of what is given to it via parms on a call
+       by the framework.
+*/
+typedef struct {
+       int             vlevel;                         // verbosity level
+       bool    forward;                        // if true, message is forwarded
+       int             stats_freq;                     // header/stats after n messages
+       std::atomic<long>       pcount; // messages processed
+       std::atomic<long>       icount; // messages ignored
+       std::atomic<int>        hdr;    // number of messages before next header
+} cb_info_t;
+
+// ----------------------------------------------------------
+
+/*
+       Dump bytes to tty.
+*/
+void dump( unsigned const char* buf, int len ) {
+       int             i;
+       int             j;
+       char    cheater[17];
+
+       fprintf( stdout, "<RD> 0000 | " );
+       j = 0;
+       for( i = 0; i < len; i++ ) {
+               cheater[j++] =  isprint( buf[i] ) ? buf[i] : '.';
+               fprintf( stdout, "%02x ", buf[i] );
+                       
+               if( j == 16 ) {
+                       cheater[j] = 0;
+                       fprintf( stdout, " | %s\n<RD> %04x | ", cheater, i+1 ); 
+                       j = 0;
+               }
+       }
+
+       if( j ) {
+               i = 16 - (i % 16);
+               for( ; i > 0; i-- ) {
+                       fprintf( stdout, "   " );
+               }
+               cheater[j] = 0;
+               fprintf( stdout, " | %s\n", cheater );  
+       }
+}
+
+/*
+       generate stats when the hdr count reaches 0. Only one active
+       thread will ever see it be exactly 0, so this is thread safe.
+*/
+void stats( cb_info_t& cbi ) {
+       int curv;                                       // current stat trigger value
+
+       curv = cbi.hdr--;
+
+       if( curv == 0 ) {                                       // stats when we reach 0
+               fprintf( stdout, "ignored: %ld  processed: %ld\n", 
+                       cbi.icount.load(), cbi.pcount.load() );
+               if( cbi.vlevel > 0 ) {
+                       fprintf( stdout, "\n     %5s %5s %2s %5s\n", 
+                               "MTYPE", "SUBID", "ST", "PLLEN" );
+               }
+
+               cbi.hdr = cbi.stats_freq;               // reset must be last
+       }
+}
+
+void cb1( Message& mbuf, int mtype, int subid, int len,
+                               Msg_component payload,  void* data ) {
+       cb_info_t*      cbi;
+       long total_count;
+
+       if( (cbi = (cb_info_t *) data) == NULL ) {
+               return;
+       }
+
+       cbi->pcount++;
+       stats( *cbi );                  // gen stats & header if needed
+
+       if( cbi->vlevel > 0 ) {
+               fprintf( stdout, "<RD> %-5d %-5d %02d %-5d \n",
+                               mtype, subid, mbuf.Get_state(), len );
+
+               if( cbi->vlevel > 1 ) {
+                       dump(  payload.get(), len > 64 ? 64 : len );
+               }
+       }
+
+       if( cbi->forward ) {
+               // forward with no change to len or payload
+               mbuf.Send_msg( Message::NO_CHANGE, NULL );
+       }
+}
+
+/*
+       registered as the default callback; it counts the
+       messages that we aren't giving details about.
+*/
+void cbd( Message& mbuf, int mtype, int subid, int len,
+                               Msg_component payload,  void* data ) {
+       cb_info_t*      cbi;
+
+       if( (cbi = (cb_info_t *) data) == NULL ) {
+               return;
+       }
+
+       cbi->icount++;
+       stats( *cbi );
+
+       if( cbi->forward ) {
+               // forward with no change to len or payload
+               mbuf.Send_msg( Message::NO_CHANGE, NULL );
+       }
+}
+
+int main( int argc, char** argv ) {
+       std::unique_ptr<Xapp> x;
+       char*   port = (char *) "4560";
+       int ai = 1;                                     // arg processing index
+       cb_info_t*      cbi;
+       int             ncb = 0;                        // number of callbacks registered
+       int             mtype;
+       int             nthreads = 1;
+
+       cbi = (cb_info_t *) malloc( sizeof( *cbi ) );
+       cbi->pcount = 0;
+       cbi->icount = 0;
+       cbi->stats_freq = 10;
+
+       ai = 1;
+       // very simple flag parsing (no error/bounds checking)
+       while( ai < argc ) {
+               if( argv[ai][0] != '-' )  {             // break on first non-flag
+                       break;
+               }
+
+               // very simple arg parsing; each must be separate -x -y not -xy.
+               switch( argv[ai][1] ) {
+                       case 'f':                                       // enable packet forwarding
+                               cbi->forward = true;
+                               break;
+
+                       case 'p':                                       // define port
+                               port = argv[ai+1];
+                               ai++;
+                               break;
+
+                       case 's':                                               // stats frequency
+                               cbi->stats_freq = atoi( argv[ai+1] );
+                               if( cbi->stats_freq < 5 ) {     // enforce sanity
+                                       cbi->stats_freq = 5;
+                               }
+                               ai++;
+                               break;
+
+                       case 't':                                               // thread count
+                               nthreads = atoi( argv[ai+1] );
+                               if( nthreads < 1 ) {
+                                       nthreads = 1;
+                               }
+                               ai++;
+                               break;
+
+                       case 'v':                       // simple verbose bump
+                               cbi->vlevel++;
+                               break;
+
+                       case 'V':                       // explicit verbose level
+                               cbi->vlevel = atoi( argv[ai+1] );
+                               ai++;
+                               break;
+
+                       default:
+                               fprintf( stderr, "unrecognised option: %s\n", argv[ai] );
+                               fprintf( stderr, "usage: %s [-f] [-p port] [-s stats-freq]  [-t thread-count] [-v | -V n] msg-type1 ... msg-typen\n", argv[0] );
+                               fprintf( stderr, "\tstats frequency is in number of messages received\n" );
+                               fprintf( stderr, "\tverbose levels (-V) 0 counts only, 1 message info 2 payload dump\n" );
+                               exit( 1 );
+               }
+
+               ai++;
+       }
+
+       cbi->hdr = cbi->stats_freq;
+       fprintf( stderr, "<RD> listening on port: %s\n", port );
+
+       // create xapp, wait for route table if forwarding
+       x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
+
+       // register callback for each type on the command line
+       while( ai < argc ) {    
+               mtype = atoi( argv[ai] );
+               ai++;
+               fprintf( stderr, "<RD> capturing messages for type %d\n", mtype );
+               x->Add_msg_cb( mtype, cb1, cbi );
+               ncb++;
+       }
+
+       if( ncb < 1 ) {
+               fprintf( stderr, "<RD> no message types specified on the command line\n" );
+               exit( 1 );
+       }
+
+       x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi );         // register default cb
+
+       fprintf( stderr, "<RD> starting driver\n" );
+       x->Run( nthreads );
+
+       // return from run() is not expected, but some compilers might
+       // compilain if there isn't a return value here.
+       return 0;
+}