4 .. This work is licensed under a Creative Commons Attribution 4.0 International License.
5 .. SPDX-License-Identifier: CC-BY-4.0
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.
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.
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.
42 Starting with version 2.0.0 the framwork introduces a
43 *namespace* of ``xapp`` for the following classes and types:
52 This is a breaking change and as such the major version was
56 Creating the xApp instance
57 --------------------------
59 The creation of the xApp instance is as simple as invoking
60 the object's constructor with two required parameters:
70 A C string (pointer to char) which defines the port that
71 RMR will open to listen for connections.
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).
87 The following code sample illustrates the simplicity of
88 creating the instance of the xApp object.
94 #include <ricxfcpp/xapp.hpp>
96 std::unique_ptr<Xapp> xapp;
97 char* listen_port = (char *) "4560"; //RMR listen port
98 bool wait4table = true; // wait for a route table
100 xapp = std::unique_ptr<Xapp>(
101 new Xapp( listen_port, wait4table ) );
104 Figure 1: Creating an xAPP instance.
106 From a compilation perspective, the following is the simple
107 compiler invocation string needed to compile and link the
108 above program (assuming that the sample code exists in a file
109 called ``man_ex1.cpp``.
114 g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread
117 The above program, while complete and capable of being
118 compiled, does nothing useful. When invoked, RMR will be
119 initialized and will begin listening for a route table;
120 blocking the return to the main program until one is
121 received. When a valid route table arrives, initialization
122 will complete and the program will exit as there is no code
123 following the instruction to create the object.
126 LISTENING FOR MESSAGES
127 ======================
129 The program in the previous example can be extended with just
130 a few lines of code to enable it to receive and process
131 messages. The application needs to register a callback
132 function for each message type which it desires to process.
134 Once registered, each time a message is received the
135 registered callback for the message type will be invoked by
142 As with most callback related systems, a callback must have a
143 well known function signature which generally passes event
144 related information and a "user" data pointer which was
145 registered with the function. The following is the prototype
146 which callback functions must be defined with:
151 void cb_name( xapp::Message& m, int mtype, int subid,
152 int payload_len, xapp::Msg_component payload,
155 Figure 2: Callback function signature
157 The parameters passed to the callback function are as
168 A reference to the Message that was received.
175 The message type (allows for disambiguation if the
176 callback is registered for multiple message types).
183 The subscription ID from the message.
190 The number of bytes which the sender has placed into the
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.)
208 A pointer to user data. This is the pointer that was
209 provided when the function was registered.
213 To illustrate the use of a callback function, the previous
214 code example has been extended to add the function, register
215 it for message types 1000 and 1001, and to invoke the
216 ``Run()`` function in the framework (explained in the next
222 #include <ricxfcpp/xapp.hpp>
223 long m1000_count = 0; // message counters, one for each type
224 long m1001_count = 0;
226 // 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 ) {
231 if( (counter = (long *) data) != NULL ) {
237 std::unique_ptr<Xapp> xapp;
238 char* listen_port = (char *) "4560";
239 bool wait4table = false;
241 xapp = std::unique_ptr<Xapp>(
242 new Xapp( listen_port, wait4table ) );
244 // 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 );
248 xapp->Run( 1 ); // start the callback driver
251 Figure 3: Callback function example.
253 As before, the program does nothing useful, but now it will
254 execute and receive messages. For this example, the same
255 function can be used to increment the appropriate counter
256 simply by providing a pointer to the counter as the user data
257 when the callback function is registered. In addition, a
258 subtle change from the previous example has been to set the
259 wait for table flag to ``false.``
261 For an xApp that is a receive only application (never sends)
262 it is not necessary to wait for RMR to receive a table from
266 Registering A Default Callback
267 ------------------------------
269 The xApp may also register a default callback function such
270 that the function will be invoked for any message that does
271 not have a registered callback. If the xAPP does not register
272 a default callback, any message which cannot be mapped to a
273 known callback function is silently dropped. A default
274 callback is registered by providing a *generic* message type
275 of ``xapp->DEFAULT_CALLBACK`` on an ``Add_msg_cb`` call.
278 The Framework Callback Driver
279 -----------------------------
281 The ``Run()`` function within the Xapp object is invoked to
282 start the callback driver, and the xApp should not expect the
283 function to return under most circumstances. The only
284 parameter that the ``Run()`` function expects is the number
285 of threads to start. For each thread requested, the framework
286 will start a listener thread which will allow received
287 messages to be processed in parallel. If supplying a value
288 greater than one, the xApp must ensure that the callback
289 functions are thread safe as it is very likely that the same
290 callback function will be invoked concurrently from multiple
297 It is very likely that most xApps will need to send messages
298 and will not operate in "receive only" mode. Sending the
299 message is a function of the message object itself and can
300 take one of two forms:
303 * Replying to the sender of a received message
305 * Sending a message (routed based on the message type and
309 When replying to the sender, the message type and
310 subscription ID are not used to determine the destination of
311 the message; RMR ensures that the message is sent back to the
312 originating xApp. The xApp may still need to change the
313 message type and/or the subscription ID in the message prior
314 to using the reply function.
316 To provide for both situations, two reply functions are
317 supported by the Message object as illustrated with the
318 following prototypes.
323 bool Send_response( int mtype, int subid, int response_len,
324 std:shared_ptr<unsigned char> response );
326 bool Send_response( int response_len, std::shared_ptr<unsigned char> response );
328 Figure 4: Reply function prototypes.
330 In the first prototype the xApp must supply the new message
331 type and subscription ID values, where the second function
332 uses the values which are currently set in the message.
333 Further, the new payload contents, and length, are supplied
334 to both functions; the framework ensures that the message is
335 large enough to accommodate the payload, reallocating it if
336 necessary, and copies the response into the message payload
337 prior to sending. Should the xApp need to change either the
338 message type, or the subscription ID, but not both, the
339 ``NO_CHANGE`` constant can be used as illustrated below.
344 msg->Send_response( xapp::Message::NO_CHANGE, xapp::Message::NO_SUBID,
345 pl_length, (unsigned char *) payload );
347 Figure 5: Send response prototype.
349 In addition to the two function prototypes for
350 ``Send_response()`` there are two additional prototypes which
351 allow the new payload to be supplied as a shared smart
352 pointer. The other parameters to these functions are
353 identical to those illustrated above, and thus are not
356 The ``Send_msg()`` set of functions supported by the Message
357 object are identical to the ``Send_response()`` functions and
363 bool Send_msg( int mtype, int subid, int payload_len,
364 std::shared_ptr<unsigned char> payload );
366 bool Send_msg( int mtype, int subid, int payload_len,
367 unsigned char* payload );
369 bool Send_msg( int payload_len,
370 std::shared_ptr<unsigned char> payload );
372 bool Send_msg( int payload_len, unsigned char* payload );
374 Figure 6: Send function prototypes.
376 Each send function accepts the message, copies in the payload
377 provided, sets the message type and subscription ID (if
378 provided), and then causes the message to be sent. The only
379 difference between the ``Send_msg()`` and
380 ``Send_response()`` functions is that the destination of the
381 message is selected based on the mapping of the message type
382 and subscription ID using the current routing table known to
386 Direct Payload Manipulation
387 ---------------------------
389 For some applications, it might be more efficient to
390 manipulate the payload portion of an Xapp Message in place,
391 rather than creating it and relying on a buffer copy when the
392 message is finally sent. To achieve this, the xApp must
393 either use the smart pointer to the payload passed to the
394 callback function, or retrieve one from the message using
395 ``Get_payload()`` when working with a message outside of a
396 callback function. Once the smart pointer is obtained, the
397 pointer's get() function can be used to directly reference
398 the payload (unsigned char) bytes.
400 When working directly with the payload, the xApp must take
401 care not to write more than the actual payload size which can
402 be extracted from the Message object using the
403 ``Get_available_size()`` function.
405 When sending a message where the payload has been directly
406 altered, and no extra buffer copy is needed, a NULL pointer
407 should be passed to the Message send function. The following
408 illustrates how the payload can be directly manipulated and
409 returned to the sender (for simplicity, there is no error
410 handling if the payload size of the received message isn't
411 large enough for the response string, the response is just
417 Msg_component payload; // smart reference
418 int pl_size; // max size of payload
420 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 );
427 Figure 7: Send message without buffer copy.
431 Sending Multiple Responses
432 --------------------------
434 It is likely that the xApp will wish to send multiple
435 responses back to the process that sent a message that
436 triggered the callback. The callback function may invoke the
437 ``Send_response()`` function multiple times before returning.
439 After each call, the Message retains the necessary
440 information to allow for a subsequent invocation to send more
441 data. It should be noted though, that after the first call to
442 ``{Send_response()`` the original payload will be lost; if
443 necessary, the xApp must make a copy of the payload before
444 the first response call is made.
450 Not all xApps will be "responders," meaning that some xApps
451 will need to send one or more messages before they can expect
452 to receive any messages back. To accomplish this, the xApp
453 must first allocate a message buffer, optionally initialising
454 the payload, and then using the message's ``Send_msg()``
455 function to send a message out. The framework's
456 ``Alloc_msg()`` function can be used to create a Message
457 object with a desired payload size.
460 FRAMEWORK PROVIDED CALLBACKS
461 ============================
463 The framework itself may provide message handling via the
464 driver such that the xApp might not need to implement some
465 message processing functionality. Initially, the C++
466 framework will provide a default callback function to handle
467 the RMR based health check messages. This callback function
468 will assume that if the message was received, and the
469 callback invoked, that all is well and will reply with an OK
470 state. If the xApp should need to override this simplistic
471 response, all it needs to do is to register its own callback
472 function for the health check message type.
478 The C++ xAPP framework provides a very lightweight json
479 parser and data hash facility. Briefly, a json hash (Jhash)
480 can be established by creating an instance of the Jhash
481 object with a string of valid json. The resulting object's
482 functions can then be used to read values from the resulting
486 Creating The Jhash Object
487 -------------------------
489 The Jhash object is created simply by passing a json string
494 #include <ricxfcpp/Jhash>
496 std::string jstring = "{ \\"tag\\": \\"Hello World\\" }";
499 jh = new Jhash( jstring.c_str() );
501 Figure 8: The creation of the Jhash object.
503 Once the Jhash object has been created any of the methods
504 described in the following paragraphs can be used to retrieve
511 Json objects can be nested, and the nesting is supported by
512 this representation. The approach taken by Jhash is a
513 "directory view" approach, where the "current directory," or
514 current *blob,* limits the scope of visible fields.
516 As an example, the json contained in figure 9, contains a
517 "root" blob and two *sub-blobs* (address and lease_info).
523 "lodge_name": "Water Buffalo Lodge 714",
525 "grand_poobah": "Larry K. Slate",
526 "attendance": [ 23, 14, 41, 38, 24 ],
528 "street": "16801 Stonway Lane",
534 "owner": "Stonegate Properties",
537 "contact:" "Kyle Limestone"
541 Figure 9: Sample json with a root and two blobs.
543 Upon creation of the Jhash object, the *root* fields,
544 ``lodge_name,`` ``member_count,`` and ``grand_poobah`` are
545 immediately available. The fields in the *sub-blobs* are
546 available only when the correct blob is selected. The code
547 sample in figure 10 illustrates how a *sub-blob* is selected.
551 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
555 Figure 10: Blob selection example.
557 Currently, the selected blob must be unset in order to select
558 a blob at the root level; unset always sets the root blob.
559 Attempting to use the ``Set_blob`` function will attempt to
560 select the named blob from the current blob, and not the
564 Simple Value Extraction
565 -----------------------
567 Simple values are the expected data types *string, value,*
568 and *boolean.* This lightweight json parser treats all values
569 as floating point numbers and does not attempt to maintain a
570 separate integer type. A fourth type, *null,* is supported to
571 allow the user to expressly check for a field which is
572 defined but has no value; as opposed to a field that was
573 completely missing from the data. The following are the
574 prototypes for the functions which allow values to be
580 std::string String( const char* name );
581 float Value( const char* name );
582 bool Bool( const char* name );
585 Each of these functions returns the value associated with the
586 field with the given *name.* If the value is missing, the
587 following default values are returned:
597 An empty string (.e.g "").
613 If the user needs to disambiguate between a missing value and
614 the default value either the ``Missing`` or ``Exists``
615 function should be used first.
618 Testing For Existing and Missing Fields
619 ---------------------------------------
621 Two functions allow the developer to determine whether or not
622 a field is included in the json. Both of these functions work
623 on the current *blob,* therefore it is important to ensure
624 that the correct blob is selected before using either of
625 these functions. The prototypes for the ``Exists`` and
626 ``Missing`` functions are below:
630 bool Exists( const char* name );
631 bool Is_missing( const char* name );
633 The ``Exists`` function returns *true* if the field name
634 exists in the json and *false* otherwise. Conversely, the
635 ``Missing`` function returns *true* when the field name does
636 not exist in the json.
642 The ``Exists`` and ``Missing`` functions might not be enough
643 for the user code to validate the data that it has. To assist
644 with this, several functions allow direct type testing on a
645 field in the current blob. The following are the prototypes
650 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 );
656 Each of these functions return *true* if the field with the
657 given name is of the type being tested for.
663 Arrays are supported in the same manner as simple field
664 values with the addition of the need to supply an array index
665 when fetching values from the object. In addition, there is a
666 *length* function which can be used to determine the number
667 of elements in the named array. The prototypes for the array
668 based functions are below:
672 int Array_len( const char* name );
674 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 );
679 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 );
684 For each of these functions the ``eidx`` is the zero based
685 element index which is to be tested or selected.
691 An array containing blobs, rather than simple field value
692 pairs, the blob must be selected prior to using it, just as a
693 sub-blob needed to be selected. The ``Set_blob_ele`` function
694 is used to do this and has the following prototype:
698 bool Set_blob_ele( const char* name, int eidx );
701 As with selecting a sub-blob, an unset must be performed
702 before selecting the next blob. Figure 11 illustrates how
703 these functions can be used to read and print values from the
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 }
715 Figure 11: Json array containing blobs.
724 len = jh->Array_len( (char *) "members" );
725 for( i = 0; i < len; i++ ) {
726 jh->Set_blob_ele( (char *) "members", i ); // select blob
728 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 );
732 jh->Unset_blob(); // back to root
735 Figure 12: Code to process the array of blobs.
739 ALARM MANAGER INTERFACE
740 =======================
742 The C++ framework provides an API which allows the xAPP to
743 easily construct and generate alarm messages. Alarm messages
744 are a special class of RMR message, allocated in a similar
745 fashion as an RMR message through the framework's
746 ``Alloc_alarm()`` function.
748 The API consists of the following function types:
758 Cause the alarm to be assigned a severity and and sent via
759 RMR message to the alarm collector process.
766 Cause a clear message to be sent to the alarm collector.
773 Cause a clear followed by a raise message to be sent to
783 The ``xapp`` function provided by the framework is used to
784 create an alarm object. Once the xAPP has an alarm object it
785 can be used to send one, or more, alarm messages to the
788 The allocation function has three prototypes which allow the
789 xAPP to create an alarm with an initial set of information as
790 is appropriate. The following are the prototypes for the
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 );
800 Figure 13: Alarm allocation prototypes.
802 Each of the allocation functions returns a unique pointer to
803 the alarm. In the simplest form (1) the alarm is initialised
804 with an empty meid (managed element ID) string, and unset
805 problem ID (-1). The second prototype allows the xAPP to
806 supply the meid, and in the third form both the problem ID
807 and the meid are used to initialise the alarm.
813 Once an alarm has been allocated, its ``Raise()`` function
814 can be used to cause the alarm to be sent to the collector.
815 The raise process allows the xAPP to perform the following
816 modifications to the alarm before sending the message:
819 * Set the alarm severity
821 * Set the problem ID value
823 * Set the alarm information string
825 * Set the additional information string
828 The following are the prototypes for the ``Raise()``
829 functions of an alarm object: ..... In its simplest form (1)
830 the ``Raise()`` function will send the alarm without making
831 any changes to the data. The final two forms allow the xAPP
832 to supply additional data which is added to the alarm before
833 sending the message. Each of the raise functions returns
834 ``true`` on success and ``false`` if the alarm message could
841 The severity is one of the ``SEV_`` constants listed below.
842 These map to alarm collector strings and insulate the xAPP
843 from any future alarm collector changes. The specific meaning
844 of these severity types are defined by the alarm collector
845 and thus no attempt is made to guess what their actual
846 meaning is. These constants are available by including
857 Figure 14: Severity constants available in alarm.hpp.
863 The problem ID is an integer which is assigned by the xAPP.
864 The framework makes no attempt to verify that it has been se,
865 nor does it attempt to validate the value. If the xAPP does
866 not set the value, ``-1`` is used.
872 The two information strings are also xAPP defined and provide
873 the information that the xAPP deems necessary and related to
874 the alarm. What the collector expects, and how these strings
875 are used, is beyond the scope of the framework to describe or
876 validate. If not supplied, empty strings are sent in the
883 The ``Clear()`` function of an alarm may be used to send a
884 clear message. In a manner similar to the ``Raise()``
885 functions, the ``Clear()`` functions allow the existing alarm
886 data to be sent without change, or for the xAPP to modify the
887 data before the message is sent to the collector. The
888 following are the prototype for these functions.
893 bool Clear( int severity, int problem, std::string info );
894 bool Clear( int severity, int problem, std::string info, std::string addional_info );
898 Figure 15: Clear function prototypes.
900 Each of the clear functions returns ``true`` on success and
901 ``false`` if the alarm message could not be sent.
903 The ``Clear_all()`` function sends a special action code to
904 the collector which is assumed to clear all alarms. However,
905 it is unknown whether that implies **all** alarms, or all
906 alarms matching the ``problem_id,`` or some other
907 interpretation. Please consult the alarm collector
908 documentation for these specifics.
911 Adjusting Alarm Contents
912 ------------------------
914 It might be necessary for the xAPP to adjust the alarm
915 contents outside of the scope of the ``Raise()`` function, or
916 to adjust data that cannot be manipulated by ``Raise().`` The
917 following are the (self explanatory) prototypes for the
918 *setter* functions which are available to the xAPP.
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 );
930 Figure 16: Alarm Setters
937 The following sections contain several example programmes
938 which are written on top of the C++ framework.
944 The RMR dump application is an example built on top of the
945 C++ xApp framework to both illustrate the use of the
946 framework, and to provide a useful diagnostic tool when
947 testing and troubleshooting xApps.
949 The RMR dump xApp isn't a traditional xApp inasmuch as its
950 goal is to listen for message types and to dump information
951 about the messages received to the TTY much as
952 ``tcpdump`` does for raw packet traffic. The full source
953 code, and Makefile, are in the ``examples`` directory of the
956 When invoked, the RMR dump program is given one or more
957 message types to listen for. A callback function is
958 registered for each, and the framework ``Run()`` function is
959 invoked to drive the process. For each recognised message,
960 and depending on the verbosity level supplied at program
961 start, information about the received message(s) is written
962 to the TTY. If the forwarding option, -f, is given on the
963 command line, and an appropriate route table is provided,
964 each received message is forwarded without change. This
965 allows for the insertion of the RMR dump program into a flow,
966 however if the ultimate receiver of a message needs to reply
967 to that message, the reply will not reach the original
968 sender, so RMR dump is not a complete "middle box"
971 The following is the code for this xAPP. Several functions,
972 which provide logic unrelated to the framework, have been
973 omitted. The full code is in the framework repository.
983 #include "ricxfcpp/xapp.hpp"
986 Information that the callback needs outside
987 of what is given to it via parms on a call
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
999 // ----------------------------------------------------------------------
1004 void dump( unsigned const char* buf, int len ) {
1009 fprintf( stdout, "<RD> 0000 | " );
1011 for( i = 0; i < len; i++ ) {
1012 cheater[j++] = isprint( buf[i] ) ? buf[i] : '.';
1013 fprintf( stdout, "%02x ", buf[i] );
1017 fprintf( stdout, " | %s\\n<RD> %04x | ", cheater, i+1 );
1024 for( ; i > 0; i-- ) {
1025 fprintf( stdout, " " );
1028 fprintf( stdout, " | %s\\n", cheater );
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.
1036 void stats( cb_info_t& cbi ) {
1037 int curv; // current stat trigger value
1041 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" );
1049 cbi.hdr = cbi.stats_freq; // reset must be last
1053 void cb1( xapp::Message& mbuf, int mtype, int subid, int len,
1054 xapp::Msg_component payload, void* data ) {
1058 if( (cbi = (cb_info_t *) data) == NULL ) {
1063 stats( *cbi ); // gen stats & header if needed
1065 if( cbi->vlevel > 0 ) {
1066 fprintf( stdout, "<RD> %-5d %-5d %02d %-5d \\n",
1067 mtype, subid, mbuf.Get_state(), len );
1069 if( cbi->vlevel > 1 ) {
1070 dump( payload.get(), len > 64 ? 64 : len );
1074 if( cbi->forward ) {
1075 // forward with no change to len or payload
1076 mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL );
1081 registered as the default callback; it counts the
1082 messages that we aren't giving details about.
1084 void cbd( xapp::Message& mbuf, int mtype, int subid, int len,
1085 xapp::Msg_component payload, void* data ) {
1088 if( (cbi = (cb_info_t *) data) == NULL ) {
1095 if( cbi->forward ) {
1096 // forward with no change to len or payload
1097 mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL );
1101 int main( int argc, char** argv ) {
1102 std::unique_ptr<Xapp> x;
1103 char* port = (char *) "4560";
1104 int ai = 1; // arg processing index
1106 int ncb = 0; // number of callbacks registered
1110 cbi = (cb_info_t *) malloc( sizeof( *cbi ) );
1113 cbi->stats_freq = 10;
1116 // very simple flag parsing (no error/bounds checking)
1117 while( ai < argc ) {
1118 if( argv[ai][0] != '-' ) { // break on first non-flag
1122 // 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;
1128 case 'p': // define port
1133 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;
1141 case 't': // thread count
1142 nthreads = atoi( argv[ai+1] );
1143 if( nthreads < 1 ) {
1149 case 'v': // simple verbose bump
1153 case 'V': // explicit verbose level
1154 cbi->vlevel = atoi( argv[ai+1] );
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",
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" );
1173 cbi->hdr = cbi->stats_freq;
1174 fprintf( stderr, "<RD> listening on port: %s\\n", port );
1176 // create xapp, wait for route table if forwarding
1177 x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
1179 // register callback for each type on the command line
1180 while( ai < argc ) {
1181 mtype = atoi( argv[ai] );
1183 fprintf( stderr, "<RD> capturing messages for type %d\\n", mtype );
1184 x->Add_msg_cb( mtype, cb1, cbi );
1189 fprintf( stderr, "<RD> no message types specified on the command line\\n" );
1193 x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi ); // register default cb
1195 fprintf( stderr, "<RD> starting driver\\n" );
1198 // return from run() is not expected, but some compilers might
1199 // compilain if there isn't a return value here.
1203 Figure 17: Simple callback application.
1209 This sample programme implements a simple message listener
1210 which registers three callback functions to process two
1211 specific message types and a default callback to handle
1212 unrecognised messages.
1214 When a message of type 1 is received, it will send two
1215 response messages back to the sender. Two messages are sent
1216 in order to illustrate that it is possible to send multiple
1217 responses using the same received message.
1219 The programme illustrates how multiple listening threads can
1220 be used, but the programme is **not** thread safe; to keep
1221 this example as simple as possible, the counters are not
1222 locked when incremented.
1229 #include "ricxfcpp/message.hpp"
1230 #include "ricxfcpp/msg_component.hpp"
1231 #include "ricxfcpp/xapp.hpp"
1233 // counts; not thread safe
1238 long cb1_lastts = 0;
1241 // 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 ) {
1247 // 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" );
1254 // just count messages
1255 void cb2( xapp::Message& mbuf, int mtype, int subid, int len,
1256 xapp::Msg_component payload, void* data ) {
1260 // 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 ) {
1266 int main( int argc, char** argv ) {
1268 char* port = (char *) "4560";
1269 int ai = 1; // arg processing index
1272 // very simple flag processing (no bounds/error checking)
1273 while( ai < argc ) {
1274 if( argv[ai][0] != '-' ) {
1278 switch( argv[ai][1] ) { // we only support -x so -xy must be -x -y
1285 nthreads = atoi( argv[ai+1] );
1293 fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
1294 fprintf( stderr, "<XAPP> starting %d threads\\n", nthreads );
1296 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 );
1301 x->Run( nthreads ); // let framework drive
1302 // control should not return
1305 Figure 18: Simple callback application.
1312 This is another very simple application which demonstrates
1313 how an application can control its own listen loop while
1314 sending messages. As with the other examples, some error
1315 checking is skipped, and short cuts have been made in order
1316 to keep the example small and to the point.
1329 #include "ricxfcpp/xapp.hpp"
1331 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
1340 int response_to = 0; // max timeout wating for a response
1341 char* port = (char *) "4555";
1343 int rmtype; // received message type
1344 int delay = 1000000; // mu-sec delay; default 1s
1347 // very simple flag processing (no bounds/error checking)
1348 while( ai < argc ) {
1349 if( argv[ai][0] != '-' ) {
1353 // we only support -x so -xy must be -x -y
1354 switch( argv[ai][1] ) {
1355 // delay between messages (mu-sec)
1357 delay = atoi( argv[ai+1] );
1366 // timeout in seconds; we need to convert to ms for rmr calls
1368 response_to = atoi( argv[ai+1] ) * 1000;
1375 fprintf( stderr, "<XAPP> response timeout set to: %d\\n", response_to );
1376 fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
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 );
1382 for( i = 0; i < 100; i++ ) {
1388 // we'll reuse a received message; get max size
1389 sz = msg->Get_available_size();
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 );
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 );
1400 // receive anything that might come back
1401 msg = xfw->Receive( response_to );
1403 rmtype = msg->Get_mtype();
1404 payload = msg->Get_payload();
1405 fprintf( stderr, "got: mtype=%d payload=(%s)\\n",
1406 rmtype, (char *) payload.get() );
1408 msg = xfw->Alloc_msg( 2048 );
1417 Figure 19: Simple looping sender application.
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.
1441 #include "ricxfcpp/xapp.hpp"
1442 #include "ricxfcpp/alarm.hpp"
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;
1450 bool received = false; // false until we've received a message
1455 int response_to = 0; // max timeout wating for a response
1456 char* port = (char *) "4555";
1458 int rmtype; // received message type
1459 int delay = 1000000; // mu-sec delay; default 1s
1462 // very simple flag processing (no bounds/error checking)
1463 while( ai < argc ) {
1464 if( argv[ai][0] != '-' ) {
1468 // we only support -x so -xy must be -x -y
1469 switch( argv[ai][1] ) {
1470 // delay between messages (mu-sec)
1472 delay = atoi( argv[ai+1] );
1481 // timeout in seconds; we need to convert to ms for rmr calls
1483 response_to = atoi( argv[ai+1] ) * 1000;
1490 fprintf( stderr, "<XAPP> response timeout set to: %d\\n", response_to );
1491 fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
1493 // 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 );
1498 // 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" );
1502 for( i = 0; i < 100; i++ ) {
1508 // we'll reuse a received message; get max size
1509 sz = msg->Get_available_size();
1511 // direct access to payload; add something silly
1512 payload = msg->Get_payload();
1513 len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i );
1515 // 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 );
1520 // receive anything that might come back
1521 msg = xfw->Receive( response_to );
1524 alarm->Clear( xapp::Alarm::SEV_MINOR, 13, "messages flowing", "" ); // clear the alarm on first received message
1528 rmtype = msg->Get_mtype();
1529 payload = msg->Get_payload();
1530 fprintf( stderr, "got: mtype=%d payload=(%s)\\n",
1531 rmtype, (char *) payload.get() );
1533 msg = xfw->Alloc_msg( 2048 );
1542 Figure 20: Simple looping sender application with alarm