Add API allowing xAPPs to send alarm messages
[ric-plt/xapp-frame-cpp.git] / examples / rmr_dump.cpp
1 // vi: ts=4 sw=4 noet:
2 /*
3 ==================================================================================
4         Copyright (c) 2020 Nokia
5         Copyright (c) 2020 AT&T Intellectual Property.
6
7    Licensed under the Apache License, Version 2.0 (the "License");
8    you may not use this file except in compliance with the License.
9    You may obtain a copy of the License at
10
11        http://www.apache.org/licenses/LICENSE-2.0
12
13    Unless required by applicable law or agreed to in writing, software
14    distributed under the License is distributed on an "AS IS" BASIS,
15    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16    See the License for the specific language governing permissions and
17    limitations under the License.
18 ==================================================================================
19 */
20
21 /*
22         Mnemonic:       rmr_dump.cpp
23         Abstract:       This is a sample xapp which will behave somewhat like tcpdump
24                                 as it receives rmr messages.  The list of message types which should
25                                 be processed are given on the command line, following any flag
26                                 options (e.g. -p port).  Any of those messages received are reported
27                                 on by the application.  The verbosity level may be used to increase
28                                 the amount of detail given for the tracked messages.
29
30                                 This writes to the TTY which is slow, so do not expect it to be able
31                                 to process and report on a high rate of messages. Also, forwarded
32                                 messages will reach the intended target, however if the target
33                                 attempts to send a response the response will come back to THIS
34                                 application, and not the message origination; this cannot be a bit
35                                 of middleware in it's current form.
36
37         Date:           25 March 2020
38         Author:         E. Scott Daniels
39
40         Caution:        This example code is pulled directly into one or more of the documents
41                                 (starting from the "start-example" tag below.  Use caution with
42                                 line lengths and contents because of the requirement that this
43                                 be documentation as well as working code.
44 */
45 // start-example
46 #include <stdio.h>
47 #include <unistd.h>
48 #include <atomic>
49
50 #include "ricxfcpp/xapp.hpp"
51
52 /*
53         Information that the callback needs outside
54         of what is given to it via parms on a call
55         by the framework.
56 */
57 typedef struct {
58         int             vlevel;             // verbosity level
59         bool    forward;            // if true, message is forwarded
60         int             stats_freq;         // header/stats after n messages
61         std::atomic<long>       pcount; // messages processed
62         std::atomic<long>       icount; // messages ignored
63         std::atomic<int>        hdr;    // number of messages before next header
64 } cb_info_t;
65
66 // ----------------------------------------------------------------------
67
68 /*
69         Dump bytes to tty.
70 */
71 void dump( unsigned const char* buf, int len ) {
72         int             i;
73         int             j;
74         char    cheater[17];
75
76         fprintf( stdout, "<RD> 0000 | " );
77         j = 0;
78         for( i = 0; i < len; i++ ) {
79                 cheater[j++] =  isprint( buf[i] ) ? buf[i] : '.';
80                 fprintf( stdout, "%02x ", buf[i] );
81
82                 if( j == 16 ) {
83                         cheater[j] = 0;
84                         fprintf( stdout, " | %s\n<RD> %04x | ", cheater, i+1 );
85                         j = 0;
86                 }
87         }
88
89         if( j ) {
90                 i = 16 - (i % 16);
91                 for( ; i > 0; i-- ) {
92                         fprintf( stdout, "   " );
93                 }
94                 cheater[j] = 0;
95                 fprintf( stdout, " | %s\n", cheater );
96         }
97 }
98
99 /*
100         generate stats when the hdr count reaches 0. Only one active
101         thread will ever see it be exactly 0, so this is thread safe.
102 */
103 void stats( cb_info_t& cbi ) {
104         int curv;                                       // current stat trigger value
105
106         curv = cbi.hdr--;
107
108         if( curv == 0 ) {                                       // stats when we reach 0
109                 fprintf( stdout, "ignored: %ld  processed: %ld\n",
110                         cbi.icount.load(), cbi.pcount.load() );
111                 if( cbi.vlevel > 0 ) {
112                         fprintf( stdout, "\n     %5s %5s %2s %5s\n",
113                                 "MTYPE", "SUBID", "ST", "PLLEN" );
114                 }
115
116                 cbi.hdr = cbi.stats_freq;               // reset must be last
117         }
118 }
119
120 void cb1( xapp::Message& mbuf, int mtype, int subid, int len,
121                                 xapp::Msg_component payload,  void* data ) {
122         cb_info_t*      cbi;
123         long total_count;
124
125         if( (cbi = (cb_info_t *) data) == NULL ) {
126                 return;
127         }
128
129         cbi->pcount++;
130         stats( *cbi );                  // gen stats & header if needed
131
132         if( cbi->vlevel > 0 ) {
133                 fprintf( stdout, "<RD> %-5d %-5d %02d %-5d \n",
134                                 mtype, subid, mbuf.Get_state(), len );
135
136                 if( cbi->vlevel > 1 ) {
137                         dump(  payload.get(), len > 64 ? 64 : len );
138                 }
139         }
140
141         if( cbi->forward ) {
142                 // forward with no change to len or payload
143                 mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL );
144         }
145 }
146
147 /*
148         registered as the default callback; it counts the
149         messages that we aren't giving details about.
150 */
151 void cbd( xapp::Message& mbuf, int mtype, int subid, int len,
152                                 xapp::Msg_component payload,  void* data ) {
153         cb_info_t*      cbi;
154
155         if( (cbi = (cb_info_t *) data) == NULL ) {
156                 return;
157         }
158
159         cbi->icount++;
160         stats( *cbi );
161
162         if( cbi->forward ) {
163                 // forward with no change to len or payload
164                 mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL );
165         }
166 }
167
168 int main( int argc, char** argv ) {
169         std::unique_ptr<Xapp> x;
170         char*   port = (char *) "4560";
171         int ai = 1;                                     // arg processing index
172         cb_info_t*      cbi;
173         int             ncb = 0;                        // number of callbacks registered
174         int             mtype;
175         int             nthreads = 1;
176
177         cbi = (cb_info_t *) malloc( sizeof( *cbi ) );
178         cbi->pcount = 0;
179         cbi->icount = 0;
180         cbi->stats_freq = 10;
181
182         ai = 1;
183         // very simple flag parsing (no error/bounds checking)
184         while( ai < argc ) {
185                 if( argv[ai][0] != '-' )  {             // break on first non-flag
186                         break;
187                 }
188
189                 // very simple arg parsing; each must be separate -x -y not -xy.
190                 switch( argv[ai][1] ) {
191                         case 'f':                                       // enable packet forwarding
192                                 cbi->forward = true;
193                                 break;
194
195                         case 'p':                                       // define port
196                                 port = argv[ai+1];
197                                 ai++;
198                                 break;
199
200                         case 's':                                               // stats frequency
201                                 cbi->stats_freq = atoi( argv[ai+1] );
202                                 if( cbi->stats_freq < 5 ) {     // enforce sanity
203                                         cbi->stats_freq = 5;
204                                 }
205                                 ai++;
206                                 break;
207
208                         case 't':                                               // thread count
209                                 nthreads = atoi( argv[ai+1] );
210                                 if( nthreads < 1 ) {
211                                         nthreads = 1;
212                                 }
213                                 ai++;
214                                 break;
215
216                         case 'v':                       // simple verbose bump
217                                 cbi->vlevel++;
218                                 break;
219
220                         case 'V':                       // explicit verbose level
221                                 cbi->vlevel = atoi( argv[ai+1] );
222                                 ai++;
223                                 break;
224
225                         default:
226                                 fprintf( stderr, "unrecognised option: %s\n", argv[ai] );
227                                 fprintf( stderr, "usage: %s [-f] [-p port] "
228                                                                 "[-s stats-freq]  [-t thread-count] "
229                                                                 "[-v | -V n] msg-type1 ... msg-typen\n",
230                                                                 argv[0] );
231                                 fprintf( stderr, "\tstats frequency is based on # of messages received\n" );
232                                 fprintf( stderr, "\tverbose levels (-V) 0 counts only, "
233                                                                 "1 message info 2 payload dump\n" );
234                                 exit( 1 );
235                 }
236
237                 ai++;
238         }
239
240         cbi->hdr = cbi->stats_freq;
241         fprintf( stderr, "<RD> listening on port: %s\n", port );
242
243         // create xapp, wait for route table if forwarding
244         x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
245
246         // register callback for each type on the command line
247         while( ai < argc ) {
248                 mtype = atoi( argv[ai] );
249                 ai++;
250                 fprintf( stderr, "<RD> capturing messages for type %d\n", mtype );
251                 x->Add_msg_cb( mtype, cb1, cbi );
252                 ncb++;
253         }
254
255         if( ncb < 1 ) {
256                 fprintf( stderr, "<RD> no message types specified on the command line\n" );
257                 exit( 1 );
258         }
259
260         x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi );         // register default cb
261
262         fprintf( stderr, "<RD> starting driver\n" );
263         x->Run( nthreads );
264
265         // return from run() is not expected, but some compilers might
266         // compilain if there isn't a return value here.
267         return 0;
268 }