blob: 17f741d4f95e7f94d4c6f2e63b466a09609d2073 [file] [log] [blame]
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001============
2USER'S GUIDE
3============
E. Scott Daniels97204c82020-06-29 15:39:57 -04004.. 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
E. Scott Daniels97204c82020-06-29 15:39:57 -040012
13
14INTRODUCTION
15============
16
17The C++ framework allows the programmer to create an xApp
18object instance, and to use that instance as the logic base.
19The xApp object provides a message level interface to the RIC
20Message Router (RMR), including the ability to register
21callback functions which the instance will drive as messages
22are received; much in the same way that an X-windows
23application is driven by the window manager for all activity.
24The xApp may also choose to use its own send/receive loop,
25and thus is not required to use the callback driver mechanism
26provided by the framework.
27
28
29THE FRAMEWORK API
30=================
31
32The C++ framework API consists of the creation of the xApp
33object, and invoking desired functions via the instance of
34the object. The following paragraphs cover the various steps
35involved to create an xApp instance, wait for a route table
36to arrive, send a message, and wait for messages to arrive.
37
38
E. Scott Daniels6ef23e12020-07-15 08:03:22 -040039The Namespace
40-------------
41
42Starting with version 2.0.0 the framwork introduces a
43*namespace* of ``xapp`` for the following classes and types:
44
45
46 * Alarm
47 * Jhash
48 * Message
49 * Msg_component
50
51
52This is a breaking change and as such the major version was
53bumpped from 1 to 2.
54
55
E. Scott Daniels97204c82020-06-29 15:39:57 -040056Creating the xApp instance
57--------------------------
58
59The creation of the xApp instance is as simple as invoking
60the object's constructor with two required parameters:
61
62
E. Scott Daniels6ef23e12020-07-15 08:03:22 -040063 .. list-table::
64 :widths: auto
65 :header-rows: 0
66 :class: borderless
E. Scott Daniels97204c82020-06-29 15:39:57 -040067
E. Scott Daniels6ef23e12020-07-15 08:03:22 -040068 * - **port**
69 -
70 A C string (pointer to char) which defines the port that
71 RMR will open to listen for connections.
E. Scott Daniels97204c82020-06-29 15:39:57 -040072
73
E. Scott Daniels6ef23e12020-07-15 08:03:22 -040074 |
E. Scott Daniels97204c82020-06-29 15:39:57 -040075
E. Scott Daniels6ef23e12020-07-15 08:03:22 -040076 * - **wait**
77 -
78 A Boolean value which indicates whether or not the
79 initialization process should wait for the arrival of a
80 valid route table before completing. When true is
81 supplied, the initialization will not complete until RMR
82 has received a valid route table (or one is located via
83 the ``RMR_SEED_RT`` environment variable).
E. Scott Daniels97204c82020-06-29 15:39:57 -040084
85
E. Scott Daniels97204c82020-06-29 15:39:57 -040086
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -040087The following code sample illustrates the simplicity of
88creating the instance of the xApp object.
E. Scott Daniels97204c82020-06-29 15:39:57 -040089
90
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -040091::
92
E. Scott Daniels6ef23e12020-07-15 08:03:22 -040093 #include <memory>
94 #include <ricxfcpp/xapp.hpp>
95 int main( ) {
96 std::unique_ptr<Xapp> xapp;
97 char* listen_port = (char *) "4560"; //RMR listen port
98 bool wait4table = true; // wait for a route table
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -040099
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400100 xapp = std::unique_ptr<Xapp>(
101 new Xapp( listen_port, wait4table ) );
102 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400103
104Figure 1: Creating an xAPP instance.
105
106From a compilation perspective, the following is the simple
107compiler invocation string needed to compile and link the
108above program (assuming that the sample code exists in a file
109called ``man_ex1.cpp``.
110
111
112::
113
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400114 g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400115
116
117The above program, while complete and capable of being
118compiled, does nothing useful. When invoked, RMR will be
119initialized and will begin listening for a route table;
120blocking the return to the main program until one is
121received. When a valid route table arrives, initialization
122will complete and the program will exit as there is no code
123following the instruction to create the object.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400124
125
126LISTENING FOR MESSAGES
127======================
128
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400129The program in the previous example can be extended with just
130a few lines of code to enable it to receive and process
131messages. The application needs to register a callback
132function for each message type which it desires to process.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400133
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400134Once registered, each time a message is received the
135registered callback for the message type will be invoked by
136the framework.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400137
138
139Callback Signature
140------------------
141
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400142As with most callback related systems, a callback must have a
143well known function signature which generally passes event
144related information and a "user" data pointer which was
145registered with the function. The following is the prototype
146which callback functions must be defined with:
E. Scott Daniels97204c82020-06-29 15:39:57 -0400147
148
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400149::
E. Scott Daniels97204c82020-06-29 15:39:57 -0400150
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400151 void cb_name( xapp::Message& m, int mtype, int subid,
152 int payload_len, xapp::Msg_component payload,
153 void* usr_data );
E. Scott Daniels97204c82020-06-29 15:39:57 -0400154
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400155Figure 2: Callback function signature
E. Scott Daniels97204c82020-06-29 15:39:57 -0400156
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400157The parameters passed to the callback function are as
158follows:
E. Scott Daniels97204c82020-06-29 15:39:57 -0400159
E. Scott Daniels97204c82020-06-29 15:39:57 -0400160
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400161 .. list-table::
162 :widths: auto
163 :header-rows: 0
164 :class: borderless
E. Scott Daniels97204c82020-06-29 15:39:57 -0400165
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400166 * - **m**
167 -
168 A reference to the Message that was received.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400169
E. Scott Daniels97204c82020-06-29 15:39:57 -0400170
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400171 |
E. Scott Daniels97204c82020-06-29 15:39:57 -0400172
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400173 * - **mtype**
174 -
175 The message type (allows for disambiguation if the
176 callback is registered for multiple message types).
E. Scott Daniels97204c82020-06-29 15:39:57 -0400177
E. Scott Daniels97204c82020-06-29 15:39:57 -0400178
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400179 |
E. Scott Daniels97204c82020-06-29 15:39:57 -0400180
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400181 * - **subid**
182 -
183 The subscription ID from the message.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400184
E. Scott Daniels97204c82020-06-29 15:39:57 -0400185
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400186 |
E. Scott Daniels97204c82020-06-29 15:39:57 -0400187
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400188 * - **payload len**
189 -
190 The number of bytes which the sender has placed into the
191 payload.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400192
E. Scott Daniels97204c82020-06-29 15:39:57 -0400193
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400194 |
E. Scott Daniels97204c82020-06-29 15:39:57 -0400195
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400196 * - **payload**
197 -
198 A direct reference (smart pointer) to the payload. (The
199 smart pointer is wrapped in a special class in order to
200 provide a custom destruction function without burdening
201 the xApp developer with that knowledge.)
E. Scott Daniels97204c82020-06-29 15:39:57 -0400202
E. Scott Daniels97204c82020-06-29 15:39:57 -0400203
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400204 |
E. Scott Daniels97204c82020-06-29 15:39:57 -0400205
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400206 * - **user data**
207 -
208 A pointer to user data. This is the pointer that was
209 provided when the function was registered.
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400210
211
212
213To illustrate the use of a callback function, the previous
214code example has been extended to add the function, register
215it for message types 1000 and 1001, and to invoke the
216``Run()`` function in the framework (explained in the next
217section).
218
219::
220
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400221 #include <memory>
222 #include <ricxfcpp/xapp.hpp>
223 long m1000_count = 0; // message counters, one for each type
224 long m1001_count = 0;
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400225
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400226 // callback function that will increase the appropriate counter
227 void cbf( xapp::Message& mbuf, int mtype, int subid, int len,
228 xapp::Msg_component payload, void* data ) {
229 long* counter;
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400230
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400231 if( (counter = (long *) data) != NULL ) {
232 (*counter)++;
233 }
234 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400235
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400236 int main( ) {
237 std::unique_ptr<Xapp> xapp;
238 char* listen_port = (char *) "4560";
239 bool wait4table = false;
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400240
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400241 xapp = std::unique_ptr<Xapp>(
242 new Xapp( listen_port, wait4table ) );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400243
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400244 // register the same callback function for both msg types
245 xapp->Add_msg_cb( 1000, cbf, (void *) &m1000_count );
246 xapp->Add_msg_cb( 1001, cbf, (void *) &m1001_count );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400247
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400248 xapp->Run( 1 ); // start the callback driver
249 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400250
251Figure 3: Callback function example.
252
253As before, the program does nothing useful, but now it will
254execute and receive messages. For this example, the same
255function can be used to increment the appropriate counter
256simply by providing a pointer to the counter as the user data
257when the callback function is registered. In addition, a
258subtle change from the previous example has been to set the
259wait for table flag to ``false.``
260
261For an xApp that is a receive only application (never sends)
262it is not necessary to wait for RMR to receive a table from
263the Route Manager.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400264
265
266Registering A Default Callback
267------------------------------
268
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400269The xApp may also register a default callback function such
270that the function will be invoked for any message that does
271not have a registered callback. If the xAPP does not register
272a default callback, any message which cannot be mapped to a
273known callback function is silently dropped. A default
274callback is registered by providing a *generic* message type
275of ``xapp->DEFAULT_CALLBACK`` on an ``Add_msg_cb`` call.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400276
277
278The Framework Callback Driver
279-----------------------------
280
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400281The ``Run()`` function within the Xapp object is invoked to
282start the callback driver, and the xApp should not expect the
283function to return under most circumstances. The only
284parameter that the ``Run()`` function expects is the number
285of threads to start. For each thread requested, the framework
286will start a listener thread which will allow received
287messages to be processed in parallel. If supplying a value
288greater than one, the xApp must ensure that the callback
289functions are thread safe as it is very likely that the same
290callback function will be invoked concurrently from multiple
291threads.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400292
293
294SENDING MESSAGES
295================
296
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400297It is very likely that most xApps will need to send messages
298and will not operate in "receive only" mode. Sending the
299message is a function of the message object itself and can
300take one of two forms:
E. Scott Daniels97204c82020-06-29 15:39:57 -0400301
302
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400303 * Replying to the sender of a received message
E. Scott Daniels97204c82020-06-29 15:39:57 -0400304
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400305 * Sending a message (routed based on the message type and
306 subscription ID)
E. Scott Daniels97204c82020-06-29 15:39:57 -0400307
308
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400309When replying to the sender, the message type and
310subscription ID are not used to determine the destination of
311the message; RMR ensures that the message is sent back to the
312originating xApp. The xApp may still need to change the
313message type and/or the subscription ID in the message prior
314to using the reply function.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400315
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400316To provide for both situations, two reply functions are
317supported by the Message object as illustrated with the
318following prototypes.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400319
320
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400321::
E. Scott Daniels97204c82020-06-29 15:39:57 -0400322
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400323 bool Send_response( int mtype, int subid, int response_len,
324 std:shared_ptr<unsigned char> response );
E. Scott Daniels97204c82020-06-29 15:39:57 -0400325
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400326 bool Send_response( int response_len, std::shared_ptr<unsigned char> response );
E. Scott Daniels97204c82020-06-29 15:39:57 -0400327
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400328Figure 4: Reply function prototypes.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400329
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400330In the first prototype the xApp must supply the new message
331type and subscription ID values, where the second function
332uses the values which are currently set in the message.
333Further, the new payload contents, and length, are supplied
334to both functions; the framework ensures that the message is
335large enough to accommodate the payload, reallocating it if
336necessary, and copies the response into the message payload
337prior to sending. Should the xApp need to change either the
338message type, or the subscription ID, but not both, the
339``NO_CHANGE`` constant can be used as illustrated below.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400340
341
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400342::
E. Scott Daniels97204c82020-06-29 15:39:57 -0400343
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400344 msg->Send_response( xapp::Message::NO_CHANGE, xapp::Message::NO_SUBID,
345 pl_length, (unsigned char *) payload );
E. Scott Daniels97204c82020-06-29 15:39:57 -0400346
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400347Figure 5: Send response prototype.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400348
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400349In addition to the two function prototypes for
350``Send_response()`` there are two additional prototypes which
351allow the new payload to be supplied as a shared smart
352pointer. The other parameters to these functions are
353identical to those illustrated above, and thus are not
354presented here.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400355
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400356The ``Send_msg()`` set of functions supported by the Message
357object are identical to the ``Send_response()`` functions and
358are shown below.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400359
360
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400361::
E. Scott Daniels97204c82020-06-29 15:39:57 -0400362
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400363 bool Send_msg( int mtype, int subid, int payload_len,
364 std::shared_ptr<unsigned char> payload );
E. Scott Daniels97204c82020-06-29 15:39:57 -0400365
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400366 bool Send_msg( int mtype, int subid, int payload_len,
367 unsigned char* payload );
E. Scott Daniels97204c82020-06-29 15:39:57 -0400368
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400369 bool Send_msg( int payload_len,
370 std::shared_ptr<unsigned char> payload );
E. Scott Daniels97204c82020-06-29 15:39:57 -0400371
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400372 bool Send_msg( int payload_len, unsigned char* payload );
E. Scott Daniels97204c82020-06-29 15:39:57 -0400373
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400374Figure 6: Send function prototypes.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400375
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400376Each send function accepts the message, copies in the payload
377provided, sets the message type and subscription ID (if
378provided), and then causes the message to be sent. The only
379difference between the ``Send_msg()`` and
380``Send_response()`` functions is that the destination of the
381message is selected based on the mapping of the message type
382and subscription ID using the current routing table known to
383RMR.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400384
385
386Direct Payload Manipulation
387---------------------------
388
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400389For some applications, it might be more efficient to
390manipulate the payload portion of an Xapp Message in place,
391rather than creating it and relying on a buffer copy when the
392message is finally sent. To achieve this, the xApp must
393either use the smart pointer to the payload passed to the
394callback function, or retrieve one from the message using
395``Get_payload()`` when working with a message outside of a
396callback function. Once the smart pointer is obtained, the
397pointer's get() function can be used to directly reference
398the payload (unsigned char) bytes.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400399
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400400When working directly with the payload, the xApp must take
401care not to write more than the actual payload size which can
402be extracted from the Message object using the
403``Get_available_size()`` function.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400404
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400405When sending a message where the payload has been directly
406altered, and no extra buffer copy is needed, a NULL pointer
407should be passed to the Message send function. The following
408illustrates how the payload can be directly manipulated and
409returned to the sender (for simplicity, there is no error
410handling if the payload size of the received message isn't
411large enough for the response string, the response is just
412not sent).
E. Scott Daniels97204c82020-06-29 15:39:57 -0400413
414
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400415::
E. Scott Daniels97204c82020-06-29 15:39:57 -0400416
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400417 Msg_component payload; // smart reference
418 int pl_size; // max size of payload
E. Scott Daniels97204c82020-06-29 15:39:57 -0400419
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400420 payload = msg->Get_payload();
421 pl_size = msg->Get_available_size();
422 if( snprintf( (char *) payload.get(), pl_size,
423 "Msg Received\\n" ) < pl_size ) {
424 msg->Send_response( M_TYPE, SID, strlen( raw_pl ), NULL );
425 }
E. Scott Daniels97204c82020-06-29 15:39:57 -0400426
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400427Figure 7: Send message without buffer copy.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400428
429
430
431Sending Multiple Responses
432--------------------------
433
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400434It is likely that the xApp will wish to send multiple
435responses back to the process that sent a message that
436triggered the callback. The callback function may invoke the
437``Send_response()`` function multiple times before returning.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400438
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400439After each call, the Message retains the necessary
440information to allow for a subsequent invocation to send more
441data. It should be noted though, that after the first call to
442``{Send_response()`` the original payload will be lost; if
443necessary, the xApp must make a copy of the payload before
444the first response call is made.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400445
446
447Message Allocation
448------------------
449
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400450Not all xApps will be "responders," meaning that some xApps
451will need to send one or more messages before they can expect
452to receive any messages back. To accomplish this, the xApp
453must first allocate a message buffer, optionally initialising
454the payload, and then using the message's ``Send_msg()``
455function to send a message out. The framework's
456``Alloc_msg()`` function can be used to create a Message
457object with a desired payload size.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400458
459
460FRAMEWORK PROVIDED CALLBACKS
461============================
462
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400463The framework itself may provide message handling via the
464driver such that the xApp might not need to implement some
465message processing functionality. Initially, the C++
466framework will provide a default callback function to handle
467the RMR based health check messages. This callback function
468will assume that if the message was received, and the
469callback invoked, that all is well and will reply with an OK
470state. If the xApp should need to override this simplistic
471response, all it needs to do is to register its own callback
472function for the health check message type.
473
474
475JSON SUPPORT
476============
477
478The C++ xAPP framework provides a very lightweight json
479parser and data hash facility. Briefly, a json hash (Jhash)
480can be established by creating an instance of the Jhash
481object with a string of valid json. The resulting object's
482functions can then be used to read values from the resulting
483hash.
484
485
486Creating The Jhash Object
487-------------------------
488
489The Jhash object is created simply by passing a json string
490to the constructor.
491
492::
493
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400494 #include <ricxfcpp/Jhash>
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400495
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400496 std::string jstring = "{ \\"tag\\": \\"Hello World\\" }";
497 Jhash* jh;
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400498
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400499 jh = new Jhash( jstring.c_str() );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400500
501Figure 8: The creation of the Jhash object.
502
503Once the Jhash object has been created any of the methods
504described in the following paragraphs can be used to retrieve
505the data:
506
507
508Json Blobs
509----------
510
511Json objects can be nested, and the nesting is supported by
512this representation. The approach taken by Jhash is a
513"directory view" approach, where the "current directory," or
514current *blob,* limits the scope of visible fields.
515
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400516As an example, the json contained in figure 9, contains a
517"root" blob and two *sub-blobs* (address and lease_info).
518
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400519
520::
521
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400522 {
523 "lodge_name": "Water Buffalo Lodge 714",
524 "member_count": 41,
525 "grand_poobah": "Larry K. Slate",
526 "attendance": [ 23, 14, 41, 38, 24 ],
527 "address": {
528 "street": "16801 Stonway Lane",
529 "suite": null,
530 "city": "Bedrock",
531 "post_code": "45701"
532 },
533 "lease_info": {
534 "owner": "Stonegate Properties",
535 "amount": 216.49,
536 "due": "monthly",
537 "contact:" "Kyle Limestone"
538 }
539 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400540
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400541Figure 9: Sample json with a root and two blobs.
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400542
543Upon creation of the Jhash object, the *root* fields,
544``lodge_name,`` ``member_count,`` and ``grand_poobah`` are
545immediately available. The fields in the *sub-blobs* are
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400546available only when the correct blob is selected. The code
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400547sample in figure 10 illustrates how a *sub-blob* is selected.
548
549::
550
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400551 jh->Set_blob( (char *) "address" ); // select address
552 jh->Unset_blob(); // return to root
553 jh->Set_blob( (char *) "lease_info" ); // select the lease blob
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400554
555Figure 10: Blob selection example.
556
557Currently, the selected blob must be unset in order to select
558a blob at the root level; unset always sets the root blob.
559Attempting to use the ``Set_blob`` function will attempt to
560select the named blob from the current blob, and not the
561root.
562
563
564Simple Value Extraction
565-----------------------
566
567Simple values are the expected data types *string, value,*
568and *boolean.* This lightweight json parser treats all values
569as floating point numbers and does not attempt to maintain a
570separate integer type. A fourth type, *null,* is supported to
571allow the user to expressly check for a field which is
572defined but has no value; as opposed to a field that was
573completely missing from the data. The following are the
574prototypes for the functions which allow values to be
575extracted:
576
577
578::
579
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400580 std::string String( const char* name );
581 float Value( const char* name );
582 bool Bool( const char* name );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400583
584
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400585Each of these functions returns the value associated with the
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400586field with the given *name.* If the value is missing, the
587following default values are returned:
588
589
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400590 .. list-table::
591 :widths: 15,80
592 :header-rows: 0
593 :class: borderless
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400594
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400595 * - **String**
596 -
597 An empty string (.e.g "").
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400598
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400599 |
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400600
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400601 * - **Value**
602 -
603 Zero (e.g 0.0)
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400604
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400605 |
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400606
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400607 * - **bool**
608 -
609 false
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400610
611
612
613If the user needs to disambiguate between a missing value and
614the default value either the ``Missing`` or ``Exists``
615function should be used first.
616
617
618Testing For Existing and Missing Fields
619---------------------------------------
620
621Two functions allow the developer to determine whether or not
622a field is included in the json. Both of these functions work
623on the current *blob,* therefore it is important to ensure
624that the correct blob is selected before using either of
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400625these functions. The prototypes for the ``Exists`` and
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400626``Missing`` functions are below:
627
628::
629
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400630 bool Exists( const char* name );
631 bool Is_missing( const char* name );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400632
633The ``Exists`` function returns *true* if the field name
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400634exists in the json and *false* otherwise. Conversely, the
635``Missing`` function returns *true* when the field name does
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400636not exist in the json.
637
638
639Testing Field Type
640------------------
641
642The ``Exists`` and ``Missing`` functions might not be enough
643for the user code to validate the data that it has. To assist
644with this, several functions allow direct type testing on a
645field in the current blob. The following are the prototypes
646for these functions:
647
648::
649
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400650 bool Is_bool( const char* name );
651 bool Is_null( const char* name );
652 bool Is_string( const char* name );
653 bool Is_value( const char* name );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400654
655
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400656Each of these functions return *true* if the field with the
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400657given name is of the type being tested for.
658
659
660Arrays
661------
662
663Arrays are supported in the same manner as simple field
664values with the addition of the need to supply an array index
665when fetching values from the object. In addition, there is a
666*length* function which can be used to determine the number
667of elements in the named array. The prototypes for the array
668based functions are below:
669
670::
671
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400672 int Array_len( const char* name );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400673
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400674 bool Is_bool_ele( const char* name, int eidx );
675 bool Is_null_ele( const char* name, int eidx );
676 bool Is_string_ele( const char* name, int eidx );
677 bool Is_value_ele( const char* name, int eidx );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400678
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400679 bool Bool_ele( const char* name, int eidx );
680 std::string String_ele( const char* name, int eidx );
681 float Value_ele( const char* name, int eidx );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400682
683
684For each of these functions the ``eidx`` is the zero based
685element index which is to be tested or selected.
686
687
688Arrays of Blobs
689---------------
690
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400691An array containing blobs, rather than simple field value
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400692pairs, the blob must be selected prior to using it, just as a
693sub-blob needed to be selected. The ``Set_blob_ele`` function
694is used to do this and has the following prototype:
695
696::
697
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400698 bool Set_blob_ele( const char* name, int eidx );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400699
700
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400701As with selecting a sub-blob, an unset must be performed
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400702before selecting the next blob. Figure 11 illustrates how
703these functions can be used to read and print values from the
704json in figure 12.
705
706::
707
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400708 "members": [
709 { "name": "Fred Flinstone", "member_num": 42 },
710 { "name": "Barney Rubble", "member_num": 48 },
711 { "name": "Larry K Slate", "member_num": 22 },
712 { "name": "Kyle Limestone", "member_num": 49 }
713 ]
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400714
715Figure 11: Json array containing blobs.
716
717
718::
719
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400720 std::string mname;
721 float mnum;
722 int len;
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400723
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400724 len = jh->Array_len( (char *) "members" );
725 for( i = 0; i < len; i++ ) {
726 jh->Set_blob_ele( (char *) "members", i ); // select blob
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400727
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400728 mname = jh->String( (char *) "name" ); // read values
729 mnum = jh->Value( (char *) "member_num" );
730 fprintf( stdout, "%s is member %d\\n", mname.c_str(), (int) mnum );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400731
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400732 jh->Unset_blob(); // back to root
733 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400734
735Figure 12: Code to process the array of blobs.
736
E. Scott Daniels97204c82020-06-29 15:39:57 -0400737
738
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400739ALARM MANAGER INTERFACE
740=======================
741
742The C++ framework provides an API which allows the xAPP to
743easily construct and generate alarm messages. Alarm messages
744are a special class of RMR message, allocated in a similar
745fashion as an RMR message through the framework's
746``Alloc_alarm()`` function.
747
748The API consists of the following function types:
749
750
751 .. list-table::
752 :widths: auto
753 :header-rows: 0
754 :class: borderless
755
756 * - **Raise**
757 -
758 Cause the alarm to be assigned a severity and and sent via
759 RMR message to the alarm collector process.
760
761
762 |
763
764 * - **Clear**
765 -
766 Cause a clear message to be sent to the alarm collector.
767
768
769 |
770
771 * - **Raise Again**
772 -
773 Cause a clear followed by a raise message to be sent to
774 the alarm collector.
775
776
777
778
779
780Allocating Alarms
781-----------------
782
783The ``xapp`` function provided by the framework is used to
784create an alarm object. Once the xAPP has an alarm object it
785can be used to send one, or more, alarm messages to the
786collector.
787
788The allocation function has three prototypes which allow the
789xAPP to create an alarm with an initial set of information as
790is appropriate. The following are the prototypes for the
791allocate functions:
792
793
794::
795
796 std::unique_ptr<xapp::Alarm> Alloc_alarm( );
797 std::unique_ptr<xapp::Alarm> Alloc_alarm( std::string meid );
798 std::unique_ptr<xapp::Alarm> Alloc_alarm( int prob_id, std::string meid );
799
800Figure 13: Alarm allocation prototypes.
801
802Each of the allocation functions returns a unique pointer to
803the alarm. In the simplest form (1) the alarm is initialised
804with an empty meid (managed element ID) string, and unset
805problem ID (-1). The second prototype allows the xAPP to
806supply the meid, and in the third form both the problem ID
807and the meid are used to initialise the alarm.
808
809
810Raising An Alarm
811----------------
812
813Once an alarm has been allocated, its ``Raise()`` function
814can be used to cause the alarm to be sent to the collector.
815The raise process allows the xAPP to perform the following
816modifications to the alarm before sending the message:
817
818
819 * Set the alarm severity
820
821 * Set the problem ID value
822
823 * Set the alarm information string
824
825 * Set the additional information string
826
827
828The following are the prototypes for the ``Raise()``
829functions of an alarm object: ..... In its simplest form (1)
830the ``Raise()`` function will send the alarm without making
831any changes to the data. The final two forms allow the xAPP
832to supply additional data which is added to the alarm before
833sending the message. Each of the raise functions returns
834``true`` on success and ``false`` if the alarm message could
835not be sent.
836
837
838Severity
839--------
840
841The severity is one of the ``SEV_`` constants listed below.
842These map to alarm collector strings and insulate the xAPP
843from any future alarm collector changes. The specific meaning
844of these severity types are defined by the alarm collector
845and thus no attempt is made to guess what their actual
846meaning is. These constants are available by including
847``alarm.hpp.``
848
849
850 ::
851
852 SEV_MAJOR
853 SEV_MINOR
854 SEV_WARN
855 SEV_DEFAULT
856
857Figure 14: Severity constants available in alarm.hpp.
858
859
860The Problem ID
861--------------
862
863The problem ID is an integer which is assigned by the xAPP.
864The framework makes no attempt to verify that it has been se,
865nor does it attempt to validate the value. If the xAPP does
866not set the value, ``-1`` is used.
867
868
869Information Strings
870-------------------
871
872The two information strings are also xAPP defined and provide
873the information that the xAPP deems necessary and related to
874the alarm. What the collector expects, and how these strings
875are used, is beyond the scope of the framework to describe or
876validate. If not supplied, empty strings are sent in the
877alarm message.
878
879
880Clearing An Alarm
881-----------------
882
883The ``Clear()`` function of an alarm may be used to send a
884clear message. In a manner similar to the ``Raise()``
885functions, the ``Clear()`` functions allow the existing alarm
886data to be sent without change, or for the xAPP to modify the
887data before the message is sent to the collector. The
888following are the prototype for these functions.
889
890::
891
892 bool Clear( );
893 bool Clear( int severity, int problem, std::string info );
894 bool Clear( int severity, int problem, std::string info, std::string addional_info );
895 bool Clear_all( );
896
897
898Figure 15: Clear function prototypes.
899
900Each of the clear functions returns ``true`` on success and
901``false`` if the alarm message could not be sent.
902
903The ``Clear_all()`` function sends a special action code to
904the collector which is assumed to clear all alarms. However,
905it is unknown whether that implies **all** alarms, or all
906alarms matching the ``problem_id,`` or some other
907interpretation. Please consult the alarm collector
908documentation for these specifics.
909
910
911Adjusting Alarm Contents
912------------------------
913
914It might be necessary for the xAPP to adjust the alarm
915contents outside of the scope of the ``Raise()`` function, or
916to adjust data that cannot be manipulated by ``Raise().`` The
917following are the (self explanatory) prototypes for the
918*setter* functions which are available to the xAPP.
919
920
921::
922
923 void Set_additional( std::string new_info );
924 void Set_appid( std::string new_id );
925 void Set_info( std::string new_info );
926 void Set_meid( std::string new_meid );
927 void Set_problem( int new_id );
928 void Set_severity( int new_sev );
929
930Figure 16: Alarm Setters
931
932
933
E. Scott Daniels97204c82020-06-29 15:39:57 -0400934EXAMPLE PROGRAMMES
935==================
936
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400937The following sections contain several example programmes
938which are written on top of the C++ framework.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400939
940
941RMR Dump xAPP
942-------------
943
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400944The RMR dump application is an example built on top of the
945C++ xApp framework to both illustrate the use of the
946framework, and to provide a useful diagnostic tool when
947testing and troubleshooting xApps.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400948
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400949The RMR dump xApp isn't a traditional xApp inasmuch as its
950goal is to listen for message types and to dump information
951about the messages received to the TTY much as
952``tcpdump`` does for raw packet traffic. The full source
953code, and Makefile, are in the ``examples`` directory of the
954C++ framework repo.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400955
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400956When invoked, the RMR dump program is given one or more
957message types to listen for. A callback function is
958registered for each, and the framework ``Run()`` function is
959invoked to drive the process. For each recognised message,
960and depending on the verbosity level supplied at program
961start, information about the received message(s) is written
962to the TTY. If the forwarding option, -f, is given on the
963command line, and an appropriate route table is provided,
964each received message is forwarded without change. This
965allows for the insertion of the RMR dump program into a flow,
966however if the ultimate receiver of a message needs to reply
967to that message, the reply will not reach the original
968sender, so RMR dump is not a complete "middle box"
969application.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400970
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -0400971The following is the code for this xAPP. Several functions,
972which provide logic unrelated to the framework, have been
973omitted. The full code is in the framework repository.
E. Scott Daniels97204c82020-06-29 15:39:57 -0400974
975
976
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400977 ::
E. Scott Daniels97204c82020-06-29 15:39:57 -0400978
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400979 #include <stdio.h>
980 #include <unistd.h>
981 #include <atomic>
E. Scott Daniels97204c82020-06-29 15:39:57 -0400982
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400983 #include "ricxfcpp/xapp.hpp"
E. Scott Daniels97204c82020-06-29 15:39:57 -0400984
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400985 /*
986 Information that the callback needs outside
987 of what is given to it via parms on a call
988 by the framework.
989 */
990 typedef struct {
991 int vlevel; // verbosity level
992 bool forward; // if true, message is forwarded
993 int stats_freq; // header/stats after n messages
994 std::atomic<long> pcount; // messages processed
995 std::atomic<long> icount; // messages ignored
996 std::atomic<int> hdr; // number of messages before next header
997 } cb_info_t;
E. Scott Daniels97204c82020-06-29 15:39:57 -0400998
E. Scott Daniels6ef23e12020-07-15 08:03:22 -0400999 // ----------------------------------------------------------------------
E. Scott Daniels97204c82020-06-29 15:39:57 -04001000
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001001 /*
1002 Dump bytes to tty.
1003 */
1004 void dump( unsigned const char* buf, int len ) {
1005 int i;
1006 int j;
1007 char cheater[17];
E. Scott Daniels97204c82020-06-29 15:39:57 -04001008
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001009 fprintf( stdout, "<RD> 0000 | " );
1010 j = 0;
1011 for( i = 0; i < len; i++ ) {
1012 cheater[j++] = isprint( buf[i] ) ? buf[i] : '.';
1013 fprintf( stdout, "%02x ", buf[i] );
E. Scott Daniels97204c82020-06-29 15:39:57 -04001014
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001015 if( j == 16 ) {
1016 cheater[j] = 0;
1017 fprintf( stdout, " | %s\\n<RD> %04x | ", cheater, i+1 );
1018 j = 0;
1019 }
1020 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001021
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001022 if( j ) {
1023 i = 16 - (i % 16);
1024 for( ; i > 0; i-- ) {
1025 fprintf( stdout, " " );
1026 }
1027 cheater[j] = 0;
1028 fprintf( stdout, " | %s\\n", cheater );
1029 }
1030 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001031
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001032 /*
1033 generate stats when the hdr count reaches 0. Only one active
1034 thread will ever see it be exactly 0, so this is thread safe.
1035 */
1036 void stats( cb_info_t& cbi ) {
1037 int curv; // current stat trigger value
E. Scott Daniels97204c82020-06-29 15:39:57 -04001038
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001039 curv = cbi.hdr--;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001040
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001041 if( curv == 0 ) { // stats when we reach 0
1042 fprintf( stdout, "ignored: %ld processed: %ld\\n",
1043 cbi.icount.load(), cbi.pcount.load() );
1044 if( cbi.vlevel > 0 ) {
1045 fprintf( stdout, "\\n %5s %5s %2s %5s\\n",
1046 "MTYPE", "SUBID", "ST", "PLLEN" );
1047 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001048
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001049 cbi.hdr = cbi.stats_freq; // reset must be last
1050 }
1051 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001052
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001053 void cb1( xapp::Message& mbuf, int mtype, int subid, int len,
1054 xapp::Msg_component payload, void* data ) {
1055 cb_info_t* cbi;
1056 long total_count;
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001057
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001058 if( (cbi = (cb_info_t *) data) == NULL ) {
1059 return;
1060 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001061
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001062 cbi->pcount++;
1063 stats( *cbi ); // gen stats & header if needed
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001064
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001065 if( cbi->vlevel > 0 ) {
1066 fprintf( stdout, "<RD> %-5d %-5d %02d %-5d \\n",
1067 mtype, subid, mbuf.Get_state(), len );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001068
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001069 if( cbi->vlevel > 1 ) {
1070 dump( payload.get(), len > 64 ? 64 : len );
1071 }
1072 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001073
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001074 if( cbi->forward ) {
1075 // forward with no change to len or payload
1076 mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL );
1077 }
1078 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001079
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001080 /*
1081 registered as the default callback; it counts the
1082 messages that we aren't giving details about.
1083 */
1084 void cbd( xapp::Message& mbuf, int mtype, int subid, int len,
1085 xapp::Msg_component payload, void* data ) {
1086 cb_info_t* cbi;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001087
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001088 if( (cbi = (cb_info_t *) data) == NULL ) {
1089 return;
1090 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001091
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001092 cbi->icount++;
1093 stats( *cbi );
E. Scott Daniels97204c82020-06-29 15:39:57 -04001094
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001095 if( cbi->forward ) {
1096 // forward with no change to len or payload
1097 mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL );
1098 }
1099 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001100
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001101 int main( int argc, char** argv ) {
1102 std::unique_ptr<Xapp> x;
1103 char* port = (char *) "4560";
1104 int ai = 1; // arg processing index
1105 cb_info_t* cbi;
1106 int ncb = 0; // number of callbacks registered
1107 int mtype;
1108 int nthreads = 1;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001109
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001110 cbi = (cb_info_t *) malloc( sizeof( *cbi ) );
1111 cbi->pcount = 0;
1112 cbi->icount = 0;
1113 cbi->stats_freq = 10;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001114
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001115 ai = 1;
1116 // very simple flag parsing (no error/bounds checking)
1117 while( ai < argc ) {
1118 if( argv[ai][0] != '-' ) { // break on first non-flag
1119 break;
1120 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001121
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001122 // very simple arg parsing; each must be separate -x -y not -xy.
1123 switch( argv[ai][1] ) {
1124 case 'f': // enable packet forwarding
1125 cbi->forward = true;
1126 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001127
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001128 case 'p': // define port
1129 port = argv[ai+1];
1130 ai++;
1131 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001132
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001133 case 's': // stats frequency
1134 cbi->stats_freq = atoi( argv[ai+1] );
1135 if( cbi->stats_freq < 5 ) { // enforce sanity
1136 cbi->stats_freq = 5;
1137 }
1138 ai++;
1139 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001140
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001141 case 't': // thread count
1142 nthreads = atoi( argv[ai+1] );
1143 if( nthreads < 1 ) {
1144 nthreads = 1;
1145 }
1146 ai++;
1147 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001148
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001149 case 'v': // simple verbose bump
1150 cbi->vlevel++;
1151 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001152
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001153 case 'V': // explicit verbose level
1154 cbi->vlevel = atoi( argv[ai+1] );
1155 ai++;
1156 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001157
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001158 default:
1159 fprintf( stderr, "unrecognised option: %s\\n", argv[ai] );
1160 fprintf( stderr, "usage: %s [-f] [-p port] "
1161 "[-s stats-freq] [-t thread-count] "
1162 "[-v | -V n] msg-type1 ... msg-typen\\n",
1163 argv[0] );
1164 fprintf( stderr, "\\tstats frequency is based on # of messages received\\n" );
1165 fprintf( stderr, "\\tverbose levels (-V) 0 counts only, "
1166 "1 message info 2 payload dump\\n" );
1167 exit( 1 );
1168 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001169
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001170 ai++;
1171 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001172
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001173 cbi->hdr = cbi->stats_freq;
1174 fprintf( stderr, "<RD> listening on port: %s\\n", port );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001175
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001176 // create xapp, wait for route table if forwarding
1177 x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001178
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001179 // register callback for each type on the command line
1180 while( ai < argc ) {
1181 mtype = atoi( argv[ai] );
1182 ai++;
1183 fprintf( stderr, "<RD> capturing messages for type %d\\n", mtype );
1184 x->Add_msg_cb( mtype, cb1, cbi );
1185 ncb++;
1186 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001187
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001188 if( ncb < 1 ) {
1189 fprintf( stderr, "<RD> no message types specified on the command line\\n" );
1190 exit( 1 );
1191 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001192
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001193 x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi ); // register default cb
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001194
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001195 fprintf( stderr, "<RD> starting driver\\n" );
1196 x->Run( nthreads );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001197
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001198 // return from run() is not expected, but some compilers might
1199 // compilain if there isn't a return value here.
1200 return 0;
1201 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001202
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001203 Figure 17: Simple callback application.
E. Scott Daniels97204c82020-06-29 15:39:57 -04001204
1205
1206Callback Receiver
1207-----------------
1208
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001209This sample programme implements a simple message listener
1210which registers three callback functions to process two
1211specific message types and a default callback to handle
1212unrecognised messages.
E. Scott Daniels97204c82020-06-29 15:39:57 -04001213
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001214When a message of type 1 is received, it will send two
1215response messages back to the sender. Two messages are sent
1216in order to illustrate that it is possible to send multiple
1217responses using the same received message.
E. Scott Daniels97204c82020-06-29 15:39:57 -04001218
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001219The programme illustrates how multiple listening threads can
1220be used, but the programme is **not** thread safe; to keep
1221this example as simple as possible, the counters are not
1222locked when incremented.
E. Scott Daniels97204c82020-06-29 15:39:57 -04001223
1224
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001225 ::
E. Scott Daniels97204c82020-06-29 15:39:57 -04001226
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001227 #include <stdio.h>
E. Scott Daniels97204c82020-06-29 15:39:57 -04001228
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001229 #include "ricxfcpp/message.hpp"
1230 #include "ricxfcpp/msg_component.hpp"
1231 #include "ricxfcpp/xapp.hpp"
E. Scott Daniels97204c82020-06-29 15:39:57 -04001232
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001233 // counts; not thread safe
1234 long cb1_count = 0;
1235 long cb2_count = 0;
1236 long cbd_count = 0;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001237
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001238 long cb1_lastts = 0;
1239 long cb1_lastc = 0;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001240
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001241 // respond with 2 messages for each type 1 received
1242 void cb1( xapp::Message& mbuf, int mtype, int subid, int len,
1243 xapp::Msg_component payload, void* data ) {
1244 long now;
1245 long total_count;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001246
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001247 // illustrate that we can use the same buffer for 2 rts calls
1248 mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK1\\n" );
1249 mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\\n" );
E. Scott Daniels97204c82020-06-29 15:39:57 -04001250
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001251 cb1_count++;
1252 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001253
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001254 // just count messages
1255 void cb2( xapp::Message& mbuf, int mtype, int subid, int len,
1256 xapp::Msg_component payload, void* data ) {
1257 cb2_count++;
1258 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001259
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001260 // default to count all unrecognised messages
1261 void cbd( xapp::Message& mbuf, int mtype, int subid, int len,
1262 xapp::Msg_component payload, void* data ) {
1263 cbd_count++;
1264 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001265
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001266 int main( int argc, char** argv ) {
1267 Xapp* x;
1268 char* port = (char *) "4560";
1269 int ai = 1; // arg processing index
1270 int nthreads = 1;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001271
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001272 // very simple flag processing (no bounds/error checking)
1273 while( ai < argc ) {
1274 if( argv[ai][0] != '-' ) {
1275 break;
1276 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001277
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001278 switch( argv[ai][1] ) { // we only support -x so -xy must be -x -y
1279 case 'p':
1280 port = argv[ai+1];
1281 ai++;
1282 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001283
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001284 case 't':
1285 nthreads = atoi( argv[ai+1] );
1286 ai++;
1287 break;
1288 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001289
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001290 ai++;
1291 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001292
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001293 fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
1294 fprintf( stderr, "<XAPP> starting %d threads\\n", nthreads );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001295
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001296 x = new Xapp( port, true );
1297 x->Add_msg_cb( 1, cb1, NULL ); // register callbacks
1298 x->Add_msg_cb( 2, cb2, NULL );
1299 x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001300
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001301 x->Run( nthreads ); // let framework drive
1302 // control should not return
1303 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001304
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001305 Figure 18: Simple callback application.
E. Scott Daniels97204c82020-06-29 15:39:57 -04001306
1307
1308
1309Looping Sender
1310--------------
1311
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001312This is another very simple application which demonstrates
1313how an application can control its own listen loop while
1314sending messages. As with the other examples, some error
1315checking is skipped, and short cuts have been made in order
1316to keep the example small and to the point.
E. Scott Daniels97204c82020-06-29 15:39:57 -04001317
1318
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001319 ::
E. Scott Daniels97204c82020-06-29 15:39:57 -04001320
1321
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001322 #include <stdio.h>
1323 #include <string.h>
1324 #include <unistd.h>
E. Scott Daniels97204c82020-06-29 15:39:57 -04001325
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001326 #include <iostream>
1327 #include <memory>
E. Scott Daniels97204c82020-06-29 15:39:57 -04001328
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001329 #include "ricxfcpp/xapp.hpp"
E. Scott Daniels97204c82020-06-29 15:39:57 -04001330
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001331 extern int main( int argc, char** argv ) {
1332 std::unique_ptr<Xapp> xfw;
1333 std::unique_ptr<xapp::Message> msg;
1334 xapp::Msg_component payload; // special type of unique pointer to the payload
E. Scott Daniels97204c82020-06-29 15:39:57 -04001335
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001336 int sz;
1337 int len;
1338 int i;
1339 int ai;
1340 int response_to = 0; // max timeout wating for a response
1341 char* port = (char *) "4555";
1342 int mtype = 0;
1343 int rmtype; // received message type
1344 int delay = 1000000; // mu-sec delay; default 1s
E. Scott Daniels97204c82020-06-29 15:39:57 -04001345
1346
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001347 // very simple flag processing (no bounds/error checking)
1348 while( ai < argc ) {
1349 if( argv[ai][0] != '-' ) {
1350 break;
1351 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001352
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001353 // we only support -x so -xy must be -x -y
1354 switch( argv[ai][1] ) {
1355 // delay between messages (mu-sec)
1356 case 'd':
1357 delay = atoi( argv[ai+1] );
1358 ai++;
1359 break;
1360
1361 case 'p':
1362 port = argv[ai+1];
1363 ai++;
1364 break;
1365
1366 // timeout in seconds; we need to convert to ms for rmr calls
1367 case 't':
1368 response_to = atoi( argv[ai+1] ) * 1000;
1369 ai++;
1370 break;
1371 }
1372 ai++;
1373 }
1374
1375 fprintf( stderr, "<XAPP> response timeout set to: %d\\n", response_to );
1376 fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
1377
1378 // get an instance and wait for a route table to be loaded
1379 xfw = std::unique_ptr<Xapp>( new Xapp( port, true ) );
1380 msg = xfw->Alloc_msg( 2048 );
1381
1382 for( i = 0; i < 100; i++ ) {
1383 mtype++;
1384 if( mtype > 10 ) {
1385 mtype = 0;
1386 }
1387
1388 // we'll reuse a received message; get max size
1389 sz = msg->Get_available_size();
1390
1391 // direct access to payload; add something silly
1392 payload = msg->Get_payload();
1393 len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i );
1394
1395 // payload updated in place, prevent copy by passing nil
1396 if ( ! msg->Send_msg( mtype, xapp::Message::NO_SUBID, len, NULL )) {
1397 fprintf( stderr, "<SNDR> send failed: %d\\n", i );
1398 }
1399
1400 // receive anything that might come back
1401 msg = xfw->Receive( response_to );
1402 if( msg != NULL ) {
1403 rmtype = msg->Get_mtype();
1404 payload = msg->Get_payload();
1405 fprintf( stderr, "got: mtype=%d payload=(%s)\\n",
1406 rmtype, (char *) payload.get() );
1407 } else {
1408 msg = xfw->Alloc_msg( 2048 );
1409 }
1410
1411 if( delay > 0 ) {
1412 usleep( delay );
1413 }
1414 }
1415 }
1416
1417 Figure 19: Simple looping sender application.
1418
1419
1420
1421Alarm Example
1422-------------
1423
1424 This is an extension of a previous example which sends an
1425 alarm during initialisation and clears the alarm as soon
1426 as messages are being received. It is unknown if this is
1427 the type of alarm that is expected at the collector, but
1428 illustrates how an alarm is allocated, raised and cleared.
1429
1430
1431 ::
1432
1433
1434 #include <stdio.h>
1435 #include <string.h>
1436 #include <unistd.h>
1437
1438 #include <iostream>
1439 #include <memory>
1440
1441 #include "ricxfcpp/xapp.hpp"
1442 #include "ricxfcpp/alarm.hpp"
1443
1444 extern int main( int argc, char** argv ) {
1445 std::unique_ptr<Xapp> xfw;
1446 std::unique_ptr<xapp::Message> msg;
1447 xapp::Msg_component payload; // special type of unique pointer to the payload
1448 std::unique_ptr<xapp::Alarm> alarm;
1449
1450 bool received = false; // false until we've received a message
1451 int sz;
1452 int len;
1453 int i;
1454 int ai = 1;
1455 int response_to = 0; // max timeout wating for a response
1456 char* port = (char *) "4555";
1457 int mtype = 0;
1458 int rmtype; // received message type
1459 int delay = 1000000; // mu-sec delay; default 1s
1460
1461
1462 // very simple flag processing (no bounds/error checking)
1463 while( ai < argc ) {
1464 if( argv[ai][0] != '-' ) {
E. Scott Daniels3a2533f2020-04-22 12:40:27 -04001465 break;
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001466 }
E. Scott Daniels97204c82020-06-29 15:39:57 -04001467
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001468 // we only support -x so -xy must be -x -y
1469 switch( argv[ai][1] ) {
1470 // delay between messages (mu-sec)
1471 case 'd':
1472 delay = atoi( argv[ai+1] );
1473 ai++;
1474 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001475
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001476 case 'p':
1477 port = argv[ai+1];
1478 ai++;
1479 break;
E. Scott Daniels97204c82020-06-29 15:39:57 -04001480
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001481 // timeout in seconds; we need to convert to ms for rmr calls
1482 case 't':
1483 response_to = atoi( argv[ai+1] ) * 1000;
1484 ai++;
1485 break;
1486 }
1487 ai++;
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001488 }
1489
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001490 fprintf( stderr, "<XAPP> response timeout set to: %d\\n", response_to );
1491 fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001492
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001493 // get an instance and wait for a route table to be loaded
1494 xfw = std::unique_ptr<Xapp>( new Xapp( port, true ) );
1495 msg = xfw->Alloc_msg( 2048 );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001496
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001497
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001498 // raise an unavilable alarm which we'll clear on the first recevied message
1499 alarm = xfw->Alloc_alarm( "meid-1234" );
1500 alarm->Raise( xapp::Alarm::SEV_MINOR, 13, "unavailable", "no data recevied" );
1501
1502 for( i = 0; i < 100; i++ ) {
1503 mtype++;
1504 if( mtype > 10 ) {
1505 mtype = 0;
1506 }
1507
1508 // we'll reuse a received message; get max size
1509 sz = msg->Get_available_size();
1510
1511 // direct access to payload; add something silly
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001512 payload = msg->Get_payload();
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001513 len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i );
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001514
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001515 // payload updated in place, prevent copy by passing nil
1516 if ( ! msg->Send_msg( mtype, xapp::Message::NO_SUBID, len, NULL )) {
1517 fprintf( stderr, "<SNDR> send failed: %d\\n", i );
1518 }
1519
1520 // receive anything that might come back
1521 msg = xfw->Receive( response_to );
1522 if( msg != NULL ) {
1523 if( ! received ) {
1524 alarm->Clear( xapp::Alarm::SEV_MINOR, 13, "messages flowing", "" ); // clear the alarm on first received message
1525 received = true;
1526 }
1527
1528 rmtype = msg->Get_mtype();
1529 payload = msg->Get_payload();
1530 fprintf( stderr, "got: mtype=%d payload=(%s)\\n",
1531 rmtype, (char *) payload.get() );
1532 } else {
1533 msg = xfw->Alloc_msg( 2048 );
1534 }
1535
1536 if( delay > 0 ) {
1537 usleep( delay );
1538 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001539 }
1540 }
E. Scott Daniels5a9d7c62020-07-09 11:56:10 -04001541
E. Scott Daniels6ef23e12020-07-15 08:03:22 -04001542 Figure 20: Simple looping sender application with alarm
1543 generation.
E. Scott Daniels97204c82020-06-29 15:39:57 -04001544