X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=blobdiff_plain;f=docs%2Fuser-guide.rst;h=17f741d4f95e7f94d4c6f2e63b466a09609d2073;hb=6ef23e1d7f7f47ccab5be6ff1b68e5cfdda80ad2;hp=e0ac2e12b3d527fa065303fef565088507621bef;hpb=8ec1e3c2dec6ba4fa83fe63e4207d47b4b0f3b3f;p=ric-plt%2Fxapp-frame-cpp.git diff --git a/docs/user-guide.rst b/docs/user-guide.rst index e0ac2e1..17f741d 100644 --- a/docs/user-guide.rst +++ b/docs/user-guide.rst @@ -36,6 +36,23 @@ involved to create an xApp instance, wait for a route table to arrive, send a message, and wait for messages to arrive. +The Namespace +------------- + +Starting with version 2.0.0 the framwork introduces a +*namespace* of ``xapp`` for the following classes and types: + + + * Alarm + * Jhash + * Message + * Msg_component + + +This is a breaking change and as such the major version was +bumpped from 1 to 2. + + Creating the xApp instance -------------------------- @@ -43,27 +60,27 @@ The creation of the xApp instance is as simple as invoking the object's constructor with two required parameters: - .. list-table:: - :widths: auto - :header-rows: 0 - :class: borderless + .. list-table:: + :widths: auto + :header-rows: 0 + :class: borderless - * - **port** - - - A C string (pointer to char) which defines the port that - RMR will open to listen for connections. + * - **port** + - + A C string (pointer to char) which defines the port that + RMR will open to listen for connections. - | + | - * - **wait** - - - A Boolean value which indicates whether or not the - initialization process should wait for the arrival of a - valid route table before completing. When true is - supplied, the initialization will not complete until RMR - has received a valid route table (or one is located via - the ``RMR_SEED_RT`` environment variable). + * - **wait** + - + A Boolean value which indicates whether or not the + initialization process should wait for the arrival of a + valid route table before completing. When true is + supplied, the initialization will not complete until RMR + has received a valid route table (or one is located via + the ``RMR_SEED_RT`` environment variable). @@ -73,16 +90,16 @@ creating the instance of the xApp object. :: - #include - #include - int main( ) { - std::unique_ptr xapp; - char* listen_port = (char *) "4560"; //RMR listen port - bool wait4table = true; // wait for a route table - - xapp = std::unique_ptr( - new Xapp( listen_port, wait4table ) ); - } + #include + #include + int main( ) { + std::unique_ptr xapp; + char* listen_port = (char *) "4560"; //RMR listen port + bool wait4table = true; // wait for a route table + + xapp = std::unique_ptr( + new Xapp( listen_port, wait4table ) ); + } Figure 1: Creating an xAPP instance. @@ -94,7 +111,7 @@ called ``man_ex1.cpp``. :: - g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread + g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread The above program, while complete and capable of being @@ -131,9 +148,9 @@ which callback functions must be defined with: :: - void cb_name( Message& m, int mtype, int subid, - int payload_len, Msg_component payload, - void* usr_data ); + void cb_name( xapp::Message& m, int mtype, int subid, + int payload_len, xapp::Msg_component payload, + void* usr_data ); Figure 2: Callback function signature @@ -141,55 +158,55 @@ The parameters passed to the callback function are as follows: - .. list-table:: - :widths: auto - :header-rows: 0 - :class: borderless + .. list-table:: + :widths: auto + :header-rows: 0 + :class: borderless - * - **m** - - - A reference to the Message that was received. + * - **m** + - + A reference to the Message that was received. - | + | - * - **mtype** - - - The message type (allows for disambiguation if the - callback is registered for multiple message types). + * - **mtype** + - + The message type (allows for disambiguation if the + callback is registered for multiple message types). - | + | - * - **subid** - - - The subscription ID from the message. + * - **subid** + - + The subscription ID from the message. - | + | - * - **payload len** - - - The number of bytes which the sender has placed into the - payload. + * - **payload len** + - + The number of bytes which the sender has placed into the + payload. - | + | - * - **payload** - - - A direct reference (smart pointer) to the payload. (The - smart pointer is wrapped in a special class in order to - provide a custom destruction function without burdening - the xApp developer with that knowledge.) + * - **payload** + - + A direct reference (smart pointer) to the payload. (The + smart pointer is wrapped in a special class in order to + provide a custom destruction function without burdening + the xApp developer with that knowledge.) - | + | - * - **user data** - - - A pointer to user data. This is the pointer that was - provided when the function was registered. + * - **user data** + - + A pointer to user data. This is the pointer that was + provided when the function was registered. @@ -201,35 +218,35 @@ section). :: - #include - #include - long m1000_count = 0; // message counters, one for each type - long m1001_count = 0; + #include + #include + long m1000_count = 0; // message counters, one for each type + long m1001_count = 0; - // callback function that will increase the appropriate counter - void cbf( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { - long* counter; + // callback function that will increase the appropriate counter + void cbf( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { + long* counter; - if( (counter = (long *) data) != NULL ) { - (*counter)++; - } - } + if( (counter = (long *) data) != NULL ) { + (*counter)++; + } + } - int main( ) { - std::unique_ptr xapp; - char* listen_port = (char *) "4560"; - bool wait4table = false; + int main( ) { + std::unique_ptr xapp; + char* listen_port = (char *) "4560"; + bool wait4table = false; - xapp = std::unique_ptr( - new Xapp( listen_port, wait4table ) ); + xapp = std::unique_ptr( + new Xapp( listen_port, wait4table ) ); - // register the same callback function for both msg types - xapp->Add_msg_cb( 1000, cbf, (void *) &m1000_count ); - xapp->Add_msg_cb( 1001, cbf, (void *) &m1001_count ); + // register the same callback function for both msg types + xapp->Add_msg_cb( 1000, cbf, (void *) &m1000_count ); + xapp->Add_msg_cb( 1001, cbf, (void *) &m1001_count ); - xapp->Run( 1 ); // start the callback driver - } + xapp->Run( 1 ); // start the callback driver + } Figure 3: Callback function example. @@ -283,10 +300,10 @@ message is a function of the message object itself and can take one of two forms: - * Replying to the sender of a received message + * Replying to the sender of a received message - * Sending a message (routed based on the message type and - subscription ID) + * Sending a message (routed based on the message type and + subscription ID) When replying to the sender, the message type and @@ -303,10 +320,10 @@ following prototypes. :: - bool Send_response( int mtype, int subid, int response_len, - std:shared_ptr response ); + bool Send_response( int mtype, int subid, int response_len, + std:shared_ptr response ); - bool Send_response( int response_len, std::shared_ptr response ); + bool Send_response( int response_len, std::shared_ptr response ); Figure 4: Reply function prototypes. @@ -324,8 +341,8 @@ message type, or the subscription ID, but not both, the :: - msg->Send_response( Message::NO_CHANGE, Message::NO_SUBID, - pl_length, (unsigned char *) payload ); + msg->Send_response( xapp::Message::NO_CHANGE, xapp::Message::NO_SUBID, + pl_length, (unsigned char *) payload ); Figure 5: Send response prototype. @@ -343,16 +360,16 @@ are shown below. :: - bool Send_msg( int mtype, int subid, int payload_len, - std::shared_ptr payload ); + bool Send_msg( int mtype, int subid, int payload_len, + std::shared_ptr payload ); - bool Send_msg( int mtype, int subid, int payload_len, - unsigned char* payload ); + bool Send_msg( int mtype, int subid, int payload_len, + unsigned char* payload ); - bool Send_msg( int payload_len, - std::shared_ptr payload ); + bool Send_msg( int payload_len, + std::shared_ptr payload ); - bool Send_msg( int payload_len, unsigned char* payload ); + bool Send_msg( int payload_len, unsigned char* payload ); Figure 6: Send function prototypes. @@ -397,15 +414,15 @@ not sent). :: - Msg_component payload; // smart reference - int pl_size; // max size of payload + Msg_component payload; // smart reference + int pl_size; // max size of payload - payload = msg->Get_payload(); - pl_size = msg->Get_available_size(); - if( snprintf( (char *) payload.get(), pl_size, - "Msg Received\\n" ) < pl_size ) { - msg->Send_response( M_TYPE, SID, strlen( raw_pl ), NULL ); - } + payload = msg->Get_payload(); + pl_size = msg->Get_available_size(); + if( snprintf( (char *) payload.get(), pl_size, + "Msg Received\\n" ) < pl_size ) { + msg->Send_response( M_TYPE, SID, strlen( raw_pl ), NULL ); + } Figure 7: Send message without buffer copy. @@ -474,12 +491,12 @@ to the constructor. :: - #include + #include - std::string jstring = "{ \\"tag\\": \\"Hello World\\" }"; - Jhash* jh; + std::string jstring = "{ \\"tag\\": \\"Hello World\\" }"; + Jhash* jh; - jh = new Jhash( jstring.c_str() ); + jh = new Jhash( jstring.c_str() ); Figure 8: The creation of the Jhash object. @@ -496,44 +513,44 @@ this representation. The approach taken by Jhash is a "directory view" approach, where the "current directory," or current *blob,* limits the scope of visible fields. -As an example, the json contained in figure jblob_fig, -contains a "root" blob and two *sub-blobs* (address and -lease_info). +As an example, the json contained in figure 9, contains a +"root" blob and two *sub-blobs* (address and lease_info). -:: - { - "lodge_name": "Water Buffalo Lodge 714", - "member_count": 41, - "grand_poobah": "Larry K. Slate", - "attendance": [ 23, 14, 41, 38, 24 ], - "address": { - "street": "16801 Stonway Lane", - "suite": null, - "city": "Bedrock", - "post_code": "45701" - }, - "lease_info": { - "owner": "Stonegate Properties", - "amount": 216.49, - "due": "monthly", - "contact:" "Kyle Limestone" - } - } +:: -Figure 9: Sample json with a root and too blobs. + { + "lodge_name": "Water Buffalo Lodge 714", + "member_count": 41, + "grand_poobah": "Larry K. Slate", + "attendance": [ 23, 14, 41, 38, 24 ], + "address": { + "street": "16801 Stonway Lane", + "suite": null, + "city": "Bedrock", + "post_code": "45701" + }, + "lease_info": { + "owner": "Stonegate Properties", + "amount": 216.49, + "due": "monthly", + "contact:" "Kyle Limestone" + } + } + +Figure 9: Sample json with a root and two blobs. Upon creation of the Jhash object, the *root* fields, ``lodge_name,`` ``member_count,`` and ``grand_poobah`` are immediately available. The fields in the *sub-blobs* are -avalable only when the correct blob is selected. The code +available only when the correct blob is selected. The code sample in figure 10 illustrates how a *sub-blob* is selected. :: - jh->Set_blob( (char *) "address" ); // select address - jh->Unset_blob(); // return to root - jh->Set_blob( (char *) "lease_info" ); // slect the lease blob + jh->Set_blob( (char *) "address" ); // select address + jh->Unset_blob(); // return to root + jh->Set_blob( (char *) "lease_info" ); // select the lease blob Figure 10: Blob selection example. @@ -560,36 +577,36 @@ extracted: :: - std::string String( const char* name ); - float Value( const char* name ); - bool Bool( const char* name ); + std::string String( const char* name ); + float Value( const char* name ); + bool Bool( const char* name ); -Each of these funcitons returns the value associated with the +Each of these functions returns the value associated with the field with the given *name.* If the value is missing, the following default values are returned: - .. list-table:: - :widths: 15,80 - :header-rows: 0 - :class: borderless + .. list-table:: + :widths: 15,80 + :header-rows: 0 + :class: borderless - * - **String** - - - An empty string (.e.g ""). + * - **String** + - + An empty string (.e.g ""). - | + | - * - **Value** - - - Zero (e.g 0.0) + * - **Value** + - + Zero (e.g 0.0) - | + | - * - **bool** - - - false + * - **bool** + - + false @@ -605,17 +622,17 @@ Two functions allow the developer to determine whether or not a field is included in the json. Both of these functions work on the current *blob,* therefore it is important to ensure that the correct blob is selected before using either of -these funcitons. The prototpyes for the ``Exists`` and +these functions. The prototypes for the ``Exists`` and ``Missing`` functions are below: :: - bool Exists( const char* name ); - bool Is_missing( const char* name ); + bool Exists( const char* name ); + bool Is_missing( const char* name ); The ``Exists`` function returns *true* if the field name -exists in the json and *false* otherwise. Conversly, the -``Missing`` funciton returns *true* when the field name does +exists in the json and *false* otherwise. Conversely, the +``Missing`` function returns *true* when the field name does not exist in the json. @@ -630,13 +647,13 @@ for these functions: :: - bool Is_bool( const char* name ); - bool Is_null( const char* name ); - bool Is_string( const char* name ); - bool Is_value( const char* name ); + bool Is_bool( const char* name ); + bool Is_null( const char* name ); + bool Is_string( const char* name ); + bool Is_value( const char* name ); -Each of these funcitons return *true* if the field with the +Each of these functions return *true* if the field with the given name is of the type being tested for. @@ -652,16 +669,16 @@ based functions are below: :: - int Array_len( const char* name ); + int Array_len( const char* name ); - bool Is_bool_ele( const char* name, int eidx ); - bool Is_null_ele( const char* name, int eidx ); - bool Is_string_ele( const char* name, int eidx ); - bool Is_value_ele( const char* name, int eidx ); + bool Is_bool_ele( const char* name, int eidx ); + bool Is_null_ele( const char* name, int eidx ); + bool Is_string_ele( const char* name, int eidx ); + bool Is_value_ele( const char* name, int eidx ); - bool Bool_ele( const char* name, int eidx ); - std::string String_ele( const char* name, int eidx ); - float Value_ele( const char* name, int eidx ); + bool Bool_ele( const char* name, int eidx ); + std::string String_ele( const char* name, int eidx ); + float Value_ele( const char* name, int eidx ); For each of these functions the ``eidx`` is the zero based @@ -671,54 +688,249 @@ element index which is to be tested or selected. Arrays of Blobs --------------- -An array containing blobs, rather than simiple field value +An array containing blobs, rather than simple field value pairs, the blob must be selected prior to using it, just as a sub-blob needed to be selected. The ``Set_blob_ele`` function is used to do this and has the following prototype: :: - bool Set_blob_ele( const char* name, int eidx ); + bool Set_blob_ele( const char* name, int eidx ); -As with selecting a sub-blob, an unset must be preformed +As with selecting a sub-blob, an unset must be performed before selecting the next blob. Figure 11 illustrates how these functions can be used to read and print values from the json in figure 12. :: - "members": [ - { "name": "Fred Flinstone", "member_num": 42 }, - { "name": "Barney Rubble", "member_num": 48 }, - { "name": "Larry K Slate", "member_num": 22 }, - { "name": "Kyle Limestone", "member_num": 49 } - ] + "members": [ + { "name": "Fred Flinstone", "member_num": 42 }, + { "name": "Barney Rubble", "member_num": 48 }, + { "name": "Larry K Slate", "member_num": 22 }, + { "name": "Kyle Limestone", "member_num": 49 } + ] Figure 11: Json array containing blobs. :: - std::string mname; - float mnum; - int len; + std::string mname; + float mnum; + int len; - len = jh->Array_len( (char *) "members" ); - for( i = 0; i < len; i++ ) { - jh->Set_blob_ele( (char *) "members", i ); // select blob + len = jh->Array_len( (char *) "members" ); + for( i = 0; i < len; i++ ) { + jh->Set_blob_ele( (char *) "members", i ); // select blob - mname = jh->String( (char *) "name" ); // read values - mnum = jh->Value( (char *) "member_num" ); - fprintf( stdout, "%s is member %d\\n", mname.c_str(), (int) mnum ); + mname = jh->String( (char *) "name" ); // read values + mnum = jh->Value( (char *) "member_num" ); + fprintf( stdout, "%s is member %d\\n", mname.c_str(), (int) mnum ); - jh->Unset_blob(); // back to root - } + jh->Unset_blob(); // back to root + } Figure 12: Code to process the array of blobs. +ALARM MANAGER INTERFACE +======================= + +The C++ framework provides an API which allows the xAPP to +easily construct and generate alarm messages. Alarm messages +are a special class of RMR message, allocated in a similar +fashion as an RMR message through the framework's +``Alloc_alarm()`` function. + +The API consists of the following function types: + + + .. list-table:: + :widths: auto + :header-rows: 0 + :class: borderless + + * - **Raise** + - + Cause the alarm to be assigned a severity and and sent via + RMR message to the alarm collector process. + + + | + + * - **Clear** + - + Cause a clear message to be sent to the alarm collector. + + + | + + * - **Raise Again** + - + Cause a clear followed by a raise message to be sent to + the alarm collector. + + + + + +Allocating Alarms +----------------- + +The ``xapp`` function provided by the framework is used to +create an alarm object. Once the xAPP has an alarm object it +can be used to send one, or more, alarm messages to the +collector. + +The allocation function has three prototypes which allow the +xAPP to create an alarm with an initial set of information as +is appropriate. The following are the prototypes for the +allocate functions: + + +:: + + std::unique_ptr Alloc_alarm( ); + std::unique_ptr Alloc_alarm( std::string meid ); + std::unique_ptr Alloc_alarm( int prob_id, std::string meid ); + +Figure 13: Alarm allocation prototypes. + +Each of the allocation functions returns a unique pointer to +the alarm. In the simplest form (1) the alarm is initialised +with an empty meid (managed element ID) string, and unset +problem ID (-1). The second prototype allows the xAPP to +supply the meid, and in the third form both the problem ID +and the meid are used to initialise the alarm. + + +Raising An Alarm +---------------- + +Once an alarm has been allocated, its ``Raise()`` function +can be used to cause the alarm to be sent to the collector. +The raise process allows the xAPP to perform the following +modifications to the alarm before sending the message: + + + * Set the alarm severity + + * Set the problem ID value + + * Set the alarm information string + + * Set the additional information string + + +The following are the prototypes for the ``Raise()`` +functions of an alarm object: ..... In its simplest form (1) +the ``Raise()`` function will send the alarm without making +any changes to the data. The final two forms allow the xAPP +to supply additional data which is added to the alarm before +sending the message. Each of the raise functions returns +``true`` on success and ``false`` if the alarm message could +not be sent. + + +Severity +-------- + +The severity is one of the ``SEV_`` constants listed below. +These map to alarm collector strings and insulate the xAPP +from any future alarm collector changes. The specific meaning +of these severity types are defined by the alarm collector +and thus no attempt is made to guess what their actual +meaning is. These constants are available by including +``alarm.hpp.`` + + + :: + + SEV_MAJOR + SEV_MINOR + SEV_WARN + SEV_DEFAULT + +Figure 14: Severity constants available in alarm.hpp. + + +The Problem ID +-------------- + +The problem ID is an integer which is assigned by the xAPP. +The framework makes no attempt to verify that it has been se, +nor does it attempt to validate the value. If the xAPP does +not set the value, ``-1`` is used. + + +Information Strings +------------------- + +The two information strings are also xAPP defined and provide +the information that the xAPP deems necessary and related to +the alarm. What the collector expects, and how these strings +are used, is beyond the scope of the framework to describe or +validate. If not supplied, empty strings are sent in the +alarm message. + + +Clearing An Alarm +----------------- + +The ``Clear()`` function of an alarm may be used to send a +clear message. In a manner similar to the ``Raise()`` +functions, the ``Clear()`` functions allow the existing alarm +data to be sent without change, or for the xAPP to modify the +data before the message is sent to the collector. The +following are the prototype for these functions. + +:: + + bool Clear( ); + bool Clear( int severity, int problem, std::string info ); + bool Clear( int severity, int problem, std::string info, std::string addional_info ); + bool Clear_all( ); + + +Figure 15: Clear function prototypes. + +Each of the clear functions returns ``true`` on success and +``false`` if the alarm message could not be sent. + +The ``Clear_all()`` function sends a special action code to +the collector which is assumed to clear all alarms. However, +it is unknown whether that implies **all** alarms, or all +alarms matching the ``problem_id,`` or some other +interpretation. Please consult the alarm collector +documentation for these specifics. + + +Adjusting Alarm Contents +------------------------ + +It might be necessary for the xAPP to adjust the alarm +contents outside of the scope of the ``Raise()`` function, or +to adjust data that cannot be manipulated by ``Raise().`` The +following are the (self explanatory) prototypes for the +*setter* functions which are available to the xAPP. + + +:: + + void Set_additional( std::string new_info ); + void Set_appid( std::string new_id ); + void Set_info( std::string new_info ); + void Set_meid( std::string new_meid ); + void Set_problem( int new_id ); + void Set_severity( int new_sev ); + +Figure 16: Alarm Setters + + + EXAMPLE PROGRAMMES ================== @@ -762,233 +974,233 @@ omitted. The full code is in the framework repository. - :: - - #include - #include - #include - - #include "ricxfcpp/xapp.hpp" - - /* - Information that the callback needs outside - of what is given to it via parms on a call - by the framework. - */ - typedef struct { - int vlevel; // verbosity level - bool forward; // if true, message is forwarded - int stats_freq; // header/stats after n messages - std::atomic pcount; // messages processed - std::atomic icount; // messages ignored - std::atomic hdr; // number of messages before next header - } cb_info_t; - - // ---------------------------------------------------------------------- - - /* - Dump bytes to tty. - */ - void dump( unsigned const char* buf, int len ) { - int i; - int j; - char cheater[17]; - - fprintf( stdout, " 0000 | " ); - j = 0; - for( i = 0; i < len; i++ ) { - cheater[j++] = isprint( buf[i] ) ? buf[i] : '.'; - fprintf( stdout, "%02x ", buf[i] ); - - if( j == 16 ) { - cheater[j] = 0; - fprintf( stdout, " | %s\\n %04x | ", cheater, i+1 ); - j = 0; - } - } - - if( j ) { - i = 16 - (i % 16); - for( ; i > 0; i-- ) { - fprintf( stdout, " " ); - } - cheater[j] = 0; - fprintf( stdout, " | %s\\n", cheater ); - } - } - - /* - generate stats when the hdr count reaches 0. Only one active - thread will ever see it be exactly 0, so this is thread safe. - */ - void stats( cb_info_t& cbi ) { - int curv; // current stat trigger value - - curv = cbi.hdr--; - - if( curv == 0 ) { // stats when we reach 0 - fprintf( stdout, "ignored: %ld processed: %ld\\n", - cbi.icount.load(), cbi.pcount.load() ); - if( cbi.vlevel > 0 ) { - fprintf( stdout, "\\n %5s %5s %2s %5s\\n", - "MTYPE", "SUBID", "ST", "PLLEN" ); - } + :: + + #include + #include + #include + + #include "ricxfcpp/xapp.hpp" + + /* + Information that the callback needs outside + of what is given to it via parms on a call + by the framework. + */ + typedef struct { + int vlevel; // verbosity level + bool forward; // if true, message is forwarded + int stats_freq; // header/stats after n messages + std::atomic pcount; // messages processed + std::atomic icount; // messages ignored + std::atomic hdr; // number of messages before next header + } cb_info_t; + + // ---------------------------------------------------------------------- + + /* + Dump bytes to tty. + */ + void dump( unsigned const char* buf, int len ) { + int i; + int j; + char cheater[17]; + + fprintf( stdout, " 0000 | " ); + j = 0; + for( i = 0; i < len; i++ ) { + cheater[j++] = isprint( buf[i] ) ? buf[i] : '.'; + fprintf( stdout, "%02x ", buf[i] ); + + if( j == 16 ) { + cheater[j] = 0; + fprintf( stdout, " | %s\\n %04x | ", cheater, i+1 ); + j = 0; + } + } - cbi.hdr = cbi.stats_freq; // reset must be last - } - } + if( j ) { + i = 16 - (i % 16); + for( ; i > 0; i-- ) { + fprintf( stdout, " " ); + } + cheater[j] = 0; + fprintf( stdout, " | %s\\n", cheater ); + } + } - void cb1( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { - cb_info_t* cbi; - long total_count; + /* + generate stats when the hdr count reaches 0. Only one active + thread will ever see it be exactly 0, so this is thread safe. + */ + void stats( cb_info_t& cbi ) { + int curv; // current stat trigger value - if( (cbi = (cb_info_t *) data) == NULL ) { - return; - } + curv = cbi.hdr--; - cbi->pcount++; - stats( *cbi ); // gen stats & header if needed + if( curv == 0 ) { // stats when we reach 0 + fprintf( stdout, "ignored: %ld processed: %ld\\n", + cbi.icount.load(), cbi.pcount.load() ); + if( cbi.vlevel > 0 ) { + fprintf( stdout, "\\n %5s %5s %2s %5s\\n", + "MTYPE", "SUBID", "ST", "PLLEN" ); + } - if( cbi->vlevel > 0 ) { - fprintf( stdout, " %-5d %-5d %02d %-5d \\n", - mtype, subid, mbuf.Get_state(), len ); + cbi.hdr = cbi.stats_freq; // reset must be last + } + } - if( cbi->vlevel > 1 ) { - dump( payload.get(), len > 64 ? 64 : len ); - } - } + void cb1( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { + cb_info_t* cbi; + long total_count; - if( cbi->forward ) { - // forward with no change to len or payload - mbuf.Send_msg( Message::NO_CHANGE, NULL ); - } - } - - /* - registered as the default callback; it counts the - messages that we aren't giving details about. - */ - void cbd( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { - cb_info_t* cbi; - - if( (cbi = (cb_info_t *) data) == NULL ) { - return; - } - - cbi->icount++; - stats( *cbi ); + if( (cbi = (cb_info_t *) data) == NULL ) { + return; + } - if( cbi->forward ) { - // forward with no change to len or payload - mbuf.Send_msg( Message::NO_CHANGE, NULL ); - } - } - - int main( int argc, char** argv ) { - std::unique_ptr x; - char* port = (char *) "4560"; - int ai = 1; // arg processing index - cb_info_t* cbi; - int ncb = 0; // number of callbacks registered - int mtype; - int nthreads = 1; - - cbi = (cb_info_t *) malloc( sizeof( *cbi ) ); - cbi->pcount = 0; - cbi->icount = 0; - cbi->stats_freq = 10; - - ai = 1; - // very simple flag parsing (no error/bounds checking) - while( ai < argc ) { - if( argv[ai][0] != '-' ) { // break on first non-flag - break; - } + cbi->pcount++; + stats( *cbi ); // gen stats & header if needed - // very simple arg parsing; each must be separate -x -y not -xy. - switch( argv[ai][1] ) { - case 'f': // enable packet forwarding - cbi->forward = true; - break; + if( cbi->vlevel > 0 ) { + fprintf( stdout, " %-5d %-5d %02d %-5d \\n", + mtype, subid, mbuf.Get_state(), len ); - case 'p': // define port - port = argv[ai+1]; - ai++; - break; + if( cbi->vlevel > 1 ) { + dump( payload.get(), len > 64 ? 64 : len ); + } + } - case 's': // stats frequency - cbi->stats_freq = atoi( argv[ai+1] ); - if( cbi->stats_freq < 5 ) { // enforce sanity - cbi->stats_freq = 5; - } - ai++; - break; + if( cbi->forward ) { + // forward with no change to len or payload + mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL ); + } + } - case 't': // thread count - nthreads = atoi( argv[ai+1] ); - if( nthreads < 1 ) { - nthreads = 1; - } - ai++; - break; + /* + registered as the default callback; it counts the + messages that we aren't giving details about. + */ + void cbd( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { + cb_info_t* cbi; - case 'v': // simple verbose bump - cbi->vlevel++; - break; + if( (cbi = (cb_info_t *) data) == NULL ) { + return; + } - case 'V': // explicit verbose level - cbi->vlevel = atoi( argv[ai+1] ); - ai++; - break; + cbi->icount++; + stats( *cbi ); - default: - fprintf( stderr, "unrecognised option: %s\\n", argv[ai] ); - fprintf( stderr, "usage: %s [-f] [-p port] " - "[-s stats-freq] [-t thread-count] " - "[-v | -V n] msg-type1 ... msg-typen\\n", - argv[0] ); - fprintf( stderr, "\\tstats frequency is based on # of messages received\\n" ); - fprintf( stderr, "\\tverbose levels (-V) 0 counts only, " - "1 message info 2 payload dump\\n" ); - exit( 1 ); - } + if( cbi->forward ) { + // forward with no change to len or payload + mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL ); + } + } - ai++; - } + int main( int argc, char** argv ) { + std::unique_ptr x; + char* port = (char *) "4560"; + int ai = 1; // arg processing index + cb_info_t* cbi; + int ncb = 0; // number of callbacks registered + int mtype; + int nthreads = 1; + + cbi = (cb_info_t *) malloc( sizeof( *cbi ) ); + cbi->pcount = 0; + cbi->icount = 0; + cbi->stats_freq = 10; + + ai = 1; + // very simple flag parsing (no error/bounds checking) + while( ai < argc ) { + if( argv[ai][0] != '-' ) { // break on first non-flag + break; + } + + // very simple arg parsing; each must be separate -x -y not -xy. + switch( argv[ai][1] ) { + case 'f': // enable packet forwarding + cbi->forward = true; + break; + + case 'p': // define port + port = argv[ai+1]; + ai++; + break; + + case 's': // stats frequency + cbi->stats_freq = atoi( argv[ai+1] ); + if( cbi->stats_freq < 5 ) { // enforce sanity + cbi->stats_freq = 5; + } + ai++; + break; + + case 't': // thread count + nthreads = atoi( argv[ai+1] ); + if( nthreads < 1 ) { + nthreads = 1; + } + ai++; + break; + + case 'v': // simple verbose bump + cbi->vlevel++; + break; + + case 'V': // explicit verbose level + cbi->vlevel = atoi( argv[ai+1] ); + ai++; + break; + + default: + fprintf( stderr, "unrecognised option: %s\\n", argv[ai] ); + fprintf( stderr, "usage: %s [-f] [-p port] " + "[-s stats-freq] [-t thread-count] " + "[-v | -V n] msg-type1 ... msg-typen\\n", + argv[0] ); + fprintf( stderr, "\\tstats frequency is based on # of messages received\\n" ); + fprintf( stderr, "\\tverbose levels (-V) 0 counts only, " + "1 message info 2 payload dump\\n" ); + exit( 1 ); + } + + ai++; + } - cbi->hdr = cbi->stats_freq; - fprintf( stderr, " listening on port: %s\\n", port ); + cbi->hdr = cbi->stats_freq; + fprintf( stderr, " listening on port: %s\\n", port ); - // create xapp, wait for route table if forwarding - x = std::unique_ptr( new Xapp( port, cbi->forward ) ); + // create xapp, wait for route table if forwarding + x = std::unique_ptr( new Xapp( port, cbi->forward ) ); - // register callback for each type on the command line - while( ai < argc ) { - mtype = atoi( argv[ai] ); - ai++; - fprintf( stderr, " capturing messages for type %d\\n", mtype ); - x->Add_msg_cb( mtype, cb1, cbi ); - ncb++; - } + // register callback for each type on the command line + while( ai < argc ) { + mtype = atoi( argv[ai] ); + ai++; + fprintf( stderr, " capturing messages for type %d\\n", mtype ); + x->Add_msg_cb( mtype, cb1, cbi ); + ncb++; + } - if( ncb < 1 ) { - fprintf( stderr, " no message types specified on the command line\\n" ); - exit( 1 ); - } + if( ncb < 1 ) { + fprintf( stderr, " no message types specified on the command line\\n" ); + exit( 1 ); + } - x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi ); // register default cb + x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi ); // register default cb - fprintf( stderr, " starting driver\\n" ); - x->Run( nthreads ); + fprintf( stderr, " starting driver\\n" ); + x->Run( nthreads ); - // return from run() is not expected, but some compilers might - // compilain if there isn't a return value here. - return 0; - } + // return from run() is not expected, but some compilers might + // compilain if there isn't a return value here. + return 0; + } - Figure 13: Simple callback application. + Figure 17: Simple callback application. Callback Receiver @@ -1010,87 +1222,87 @@ this example as simple as possible, the counters are not locked when incremented. - :: + :: - #include + #include - #include "ricxfcpp/message.hpp" - #include "ricxfcpp/msg_component.hpp" - #include "ricxfcpp/xapp.hpp" + #include "ricxfcpp/message.hpp" + #include "ricxfcpp/msg_component.hpp" + #include "ricxfcpp/xapp.hpp" - // counts; not thread safe - long cb1_count = 0; - long cb2_count = 0; - long cbd_count = 0; + // counts; not thread safe + long cb1_count = 0; + long cb2_count = 0; + long cbd_count = 0; - long cb1_lastts = 0; - long cb1_lastc = 0; + long cb1_lastts = 0; + long cb1_lastc = 0; - // respond with 2 messages for each type 1 received - void cb1( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { - long now; - long total_count; + // respond with 2 messages for each type 1 received + void cb1( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { + long now; + long total_count; - // illustrate that we can use the same buffer for 2 rts calls - mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK1\\n" ); - mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\\n" ); + // illustrate that we can use the same buffer for 2 rts calls + mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK1\\n" ); + mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\\n" ); - cb1_count++; - } - - // just count messages - void cb2( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { - cb2_count++; - } - - // default to count all unrecognised messages - void cbd( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { - cbd_count++; - } - - int main( int argc, char** argv ) { - Xapp* x; - char* port = (char *) "4560"; - int ai = 1; // arg processing index - int nthreads = 1; - - // very simple flag processing (no bounds/error checking) - while( ai < argc ) { - if( argv[ai][0] != '-' ) { - break; - } + cb1_count++; + } - switch( argv[ai][1] ) { // we only support -x so -xy must be -x -y - case 'p': - port = argv[ai+1]; - ai++; - break; + // just count messages + void cb2( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { + cb2_count++; + } - case 't': - nthreads = atoi( argv[ai+1] ); - ai++; - break; - } + // default to count all unrecognised messages + void cbd( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { + cbd_count++; + } - ai++; - } + int main( int argc, char** argv ) { + Xapp* x; + char* port = (char *) "4560"; + int ai = 1; // arg processing index + int nthreads = 1; + + // very simple flag processing (no bounds/error checking) + while( ai < argc ) { + if( argv[ai][0] != '-' ) { + break; + } + + switch( argv[ai][1] ) { // we only support -x so -xy must be -x -y + case 'p': + port = argv[ai+1]; + ai++; + break; + + case 't': + nthreads = atoi( argv[ai+1] ); + ai++; + break; + } + + ai++; + } - fprintf( stderr, " listening on port: %s\\n", port ); - fprintf( stderr, " starting %d threads\\n", nthreads ); + fprintf( stderr, " listening on port: %s\\n", port ); + fprintf( stderr, " starting %d threads\\n", nthreads ); - x = new Xapp( port, true ); - x->Add_msg_cb( 1, cb1, NULL ); // register callbacks - x->Add_msg_cb( 2, cb2, NULL ); - x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL ); + x = new Xapp( port, true ); + x->Add_msg_cb( 1, cb1, NULL ); // register callbacks + x->Add_msg_cb( 2, cb2, NULL ); + x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL ); - x->Run( nthreads ); // let framework drive - // control should not return - } + x->Run( nthreads ); // let framework drive + // control should not return + } - Figure 14: Simple callback application. + Figure 18: Simple callback application. @@ -1104,103 +1316,229 @@ checking is skipped, and short cuts have been made in order to keep the example small and to the point. - :: + :: + + #include + #include + #include - #include - #include - #include + #include + #include - #include - #include + #include "ricxfcpp/xapp.hpp" + + extern int main( int argc, char** argv ) { + std::unique_ptr xfw; + std::unique_ptr msg; + xapp::Msg_component payload; // special type of unique pointer to the payload + + int sz; + int len; + int i; + int ai; + int response_to = 0; // max timeout wating for a response + char* port = (char *) "4555"; + int mtype = 0; + int rmtype; // received message type + int delay = 1000000; // mu-sec delay; default 1s + + + // very simple flag processing (no bounds/error checking) + while( ai < argc ) { + if( argv[ai][0] != '-' ) { + break; + } + + // we only support -x so -xy must be -x -y + switch( argv[ai][1] ) { + // delay between messages (mu-sec) + case 'd': + delay = atoi( argv[ai+1] ); + ai++; + break; + + case 'p': + port = argv[ai+1]; + ai++; + break; + + // timeout in seconds; we need to convert to ms for rmr calls + case 't': + response_to = atoi( argv[ai+1] ) * 1000; + ai++; + break; + } + ai++; + } - #include "ricxfcpp/xapp.hpp" + fprintf( stderr, " response timeout set to: %d\\n", response_to ); + fprintf( stderr, " listening on port: %s\\n", port ); + + // get an instance and wait for a route table to be loaded + xfw = std::unique_ptr( new Xapp( port, true ) ); + msg = xfw->Alloc_msg( 2048 ); + + for( i = 0; i < 100; i++ ) { + mtype++; + if( mtype > 10 ) { + mtype = 0; + } + + // we'll reuse a received message; get max size + sz = msg->Get_available_size(); + + // direct access to payload; add something silly + payload = msg->Get_payload(); + len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i ); + + // payload updated in place, prevent copy by passing nil + if ( ! msg->Send_msg( mtype, xapp::Message::NO_SUBID, len, NULL )) { + fprintf( stderr, " send failed: %d\\n", i ); + } + + // receive anything that might come back + msg = xfw->Receive( response_to ); + if( msg != NULL ) { + rmtype = msg->Get_mtype(); + payload = msg->Get_payload(); + fprintf( stderr, "got: mtype=%d payload=(%s)\\n", + rmtype, (char *) payload.get() ); + } else { + msg = xfw->Alloc_msg( 2048 ); + } + + if( delay > 0 ) { + usleep( delay ); + } + } + } - extern int main( int argc, char** argv ) { - std::unique_ptr xfw; - std::unique_ptr msg; - Msg_component payload; // special type of unique pointer to the payload + Figure 19: Simple looping sender application. - int sz; - int len; - int i; - int ai; - int response_to = 0; // max timeout wating for a response - char* port = (char *) "4555"; - int mtype = 0; - int rmtype; // received message type - int delay = 1000000; // mu-sec delay; default 1s - // very simple flag processing (no bounds/error checking) - while( ai < argc ) { - if( argv[ai][0] != '-' ) { - break; - } +Alarm Example +------------- - // we only support -x so -xy must be -x -y - switch( argv[ai][1] ) { - // delay between messages (mu-sec) - case 'd': - delay = atoi( argv[ai+1] ); - ai++; - break; + This is an extension of a previous example which sends an + alarm during initialisation and clears the alarm as soon + as messages are being received. It is unknown if this is + the type of alarm that is expected at the collector, but + illustrates how an alarm is allocated, raised and cleared. - case 'p': - port = argv[ai+1]; - ai++; - break; - // timeout in seconds; we need to convert to ms for rmr calls - case 't': - response_to = atoi( argv[ai+1] ) * 1000; - ai++; + :: + + + #include + #include + #include + + #include + #include + + #include "ricxfcpp/xapp.hpp" + #include "ricxfcpp/alarm.hpp" + + extern int main( int argc, char** argv ) { + std::unique_ptr xfw; + std::unique_ptr msg; + xapp::Msg_component payload; // special type of unique pointer to the payload + std::unique_ptr alarm; + + bool received = false; // false until we've received a message + int sz; + int len; + int i; + int ai = 1; + int response_to = 0; // max timeout wating for a response + char* port = (char *) "4555"; + int mtype = 0; + int rmtype; // received message type + int delay = 1000000; // mu-sec delay; default 1s + + + // very simple flag processing (no bounds/error checking) + while( ai < argc ) { + if( argv[ai][0] != '-' ) { break; + } + + // we only support -x so -xy must be -x -y + switch( argv[ai][1] ) { + // delay between messages (mu-sec) + case 'd': + delay = atoi( argv[ai+1] ); + ai++; + break; + + case 'p': + port = argv[ai+1]; + ai++; + break; + + // timeout in seconds; we need to convert to ms for rmr calls + case 't': + response_to = atoi( argv[ai+1] ) * 1000; + ai++; + break; + } + ai++; } - ai++; - } - fprintf( stderr, " response timeout set to: %d\\n", response_to ); - fprintf( stderr, " listening on port: %s\\n", port ); + fprintf( stderr, " response timeout set to: %d\\n", response_to ); + fprintf( stderr, " listening on port: %s\\n", port ); - // get an instance and wait for a route table to be loaded - xfw = std::unique_ptr( new Xapp( port, true ) ); - msg = xfw->Alloc_msg( 2048 ); + // get an instance and wait for a route table to be loaded + xfw = std::unique_ptr( new Xapp( port, true ) ); + msg = xfw->Alloc_msg( 2048 ); - for( i = 0; i < 100; i++ ) { - mtype++; - if( mtype > 10 ) { - mtype = 0; - } - // we'll reuse a received message; get max size - sz = msg->Get_available_size(); + // raise an unavilable alarm which we'll clear on the first recevied message + alarm = xfw->Alloc_alarm( "meid-1234" ); + alarm->Raise( xapp::Alarm::SEV_MINOR, 13, "unavailable", "no data recevied" ); - // direct access to payload; add something silly - payload = msg->Get_payload(); - len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i ); + for( i = 0; i < 100; i++ ) { + mtype++; + if( mtype > 10 ) { + mtype = 0; + } - // payload updated in place, prevent copy by passing nil - if ( ! msg->Send_msg( mtype, Message::NO_SUBID, len, NULL )) { - fprintf( stderr, " send failed: %d\\n", i ); - } + // we'll reuse a received message; get max size + sz = msg->Get_available_size(); - // receive anything that might come back - msg = xfw->Receive( response_to ); - if( msg != NULL ) { - rmtype = msg->Get_mtype(); + // direct access to payload; add something silly payload = msg->Get_payload(); - fprintf( stderr, "got: mtype=%d payload=(%s)\\n", - rmtype, (char *) payload.get() ); - } else { - msg = xfw->Alloc_msg( 2048 ); - } + len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i ); + + // payload updated in place, prevent copy by passing nil + if ( ! msg->Send_msg( mtype, xapp::Message::NO_SUBID, len, NULL )) { + fprintf( stderr, " send failed: %d\\n", i ); + } + + // receive anything that might come back + msg = xfw->Receive( response_to ); + if( msg != NULL ) { + if( ! received ) { + alarm->Clear( xapp::Alarm::SEV_MINOR, 13, "messages flowing", "" ); // clear the alarm on first received message + received = true; + } - if( delay > 0 ) { - usleep( delay ); + rmtype = msg->Get_mtype(); + payload = msg->Get_payload(); + fprintf( stderr, "got: mtype=%d payload=(%s)\\n", + rmtype, (char *) payload.get() ); + } else { + msg = xfw->Alloc_msg( 2048 ); + } + + if( delay > 0 ) { + usleep( delay ); + } } } - } - Figure 15: Simple looping sender application. + Figure 20: Simple looping sender application with alarm + generation.