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
--------------------------
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).
::
- #include <memory>
- #include <ricxfcpp/xapp.hpp>
- int main( ) {
- std::unique_ptr<Xapp> xapp;
- char* listen_port = (char *) "4560"; //RMR listen port
- bool wait4table = true; // wait for a route table
-
- xapp = std::unique_ptr<Xapp>(
- new Xapp( listen_port, wait4table ) );
- }
+ #include <memory>
+ #include <ricxfcpp/xapp.hpp>
+ int main( ) {
+ std::unique_ptr<Xapp> xapp;
+ char* listen_port = (char *) "4560"; //RMR listen port
+ bool wait4table = true; // wait for a route table
+
+ xapp = std::unique_ptr<Xapp>(
+ new Xapp( listen_port, wait4table ) );
+ }
Figure 1: Creating an xAPP instance.
::
- 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
::
- 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
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.
::
- #include <memory>
- #include <ricxfcpp/xapp.hpp>
- long m1000_count = 0; // message counters, one for each type
- long m1001_count = 0;
+ #include <memory>
+ #include <ricxfcpp/xapp.hpp>
+ 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> xapp;
- char* listen_port = (char *) "4560";
- bool wait4table = false;
+ int main( ) {
+ std::unique_ptr<Xapp> xapp;
+ char* listen_port = (char *) "4560";
+ bool wait4table = false;
- xapp = std::unique_ptr<Xapp>(
- new Xapp( listen_port, wait4table ) );
+ xapp = std::unique_ptr<Xapp>(
+ 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.
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
::
- bool Send_response( int mtype, int subid, int response_len,
- std:shared_ptr<unsigned char> response );
+ bool Send_response( int mtype, int subid, int response_len,
+ std:shared_ptr<unsigned char> response );
- bool Send_response( int response_len, std::shared_ptr<unsigned char> response );
+ bool Send_response( int response_len, std::shared_ptr<unsigned char> response );
Figure 4: Reply function prototypes.
::
- 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.
::
- bool Send_msg( int mtype, int subid, int payload_len,
- std::shared_ptr<unsigned char> payload );
+ bool Send_msg( int mtype, int subid, int payload_len,
+ std::shared_ptr<unsigned char> 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<unsigned char> payload );
+ bool Send_msg( int payload_len,
+ std::shared_ptr<unsigned char> payload );
- bool Send_msg( int payload_len, unsigned char* payload );
+ bool Send_msg( int payload_len, unsigned char* payload );
Figure 6: Send function prototypes.
::
- 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.
::
- #include <ricxfcpp/Jhash>
+ #include <ricxfcpp/Jhash>
- 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.
"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.
::
- 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
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.
::
- 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.
::
- 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
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<xapp::Alarm> Alloc_alarm( );
+ std::unique_ptr<xapp::Alarm> Alloc_alarm( std::string meid );
+ std::unique_ptr<xapp::Alarm> 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
==================
- ::
-
- #include <stdio.h>
- #include <unistd.h>
- #include <atomic>
-
- #include "ricxfcpp/xapp.hpp"
-
- /*
- Information that the callback needs outside
- of what is given to it via parms on a call
- by the framework.
- */
- typedef struct {
- int vlevel; // verbosity level
- bool forward; // if true, message is forwarded
- int stats_freq; // header/stats after n messages
- std::atomic<long> pcount; // messages processed
- std::atomic<long> icount; // messages ignored
- std::atomic<int> hdr; // number of messages before next header
- } cb_info_t;
-
- // ----------------------------------------------------------------------
-
- /*
- Dump bytes to tty.
- */
- void dump( unsigned const char* buf, int len ) {
- int i;
- int j;
- char cheater[17];
-
- fprintf( stdout, "<RD> 0000 | " );
- j = 0;
- for( i = 0; i < len; i++ ) {
- cheater[j++] = isprint( buf[i] ) ? buf[i] : '.';
- fprintf( stdout, "%02x ", buf[i] );
-
- if( j == 16 ) {
- cheater[j] = 0;
- fprintf( stdout, " | %s\\n<RD> %04x | ", cheater, i+1 );
- j = 0;
- }
- }
-
- if( j ) {
- i = 16 - (i % 16);
- for( ; i > 0; i-- ) {
- fprintf( stdout, " " );
- }
- cheater[j] = 0;
- fprintf( stdout, " | %s\\n", cheater );
- }
- }
-
- /*
- generate stats when the hdr count reaches 0. Only one active
- thread will ever see it be exactly 0, so this is thread safe.
- */
- void stats( cb_info_t& cbi ) {
- int curv; // current stat trigger value
-
- curv = cbi.hdr--;
-
- if( curv == 0 ) { // stats when we reach 0
- fprintf( stdout, "ignored: %ld processed: %ld\\n",
- cbi.icount.load(), cbi.pcount.load() );
- if( cbi.vlevel > 0 ) {
- fprintf( stdout, "\\n %5s %5s %2s %5s\\n",
- "MTYPE", "SUBID", "ST", "PLLEN" );
- }
+ ::
+
+ #include <stdio.h>
+ #include <unistd.h>
+ #include <atomic>
+
+ #include "ricxfcpp/xapp.hpp"
+
+ /*
+ Information that the callback needs outside
+ of what is given to it via parms on a call
+ by the framework.
+ */
+ typedef struct {
+ int vlevel; // verbosity level
+ bool forward; // if true, message is forwarded
+ int stats_freq; // header/stats after n messages
+ std::atomic<long> pcount; // messages processed
+ std::atomic<long> icount; // messages ignored
+ std::atomic<int> hdr; // number of messages before next header
+ } cb_info_t;
+
+ // ----------------------------------------------------------------------
+
+ /*
+ Dump bytes to tty.
+ */
+ void dump( unsigned const char* buf, int len ) {
+ int i;
+ int j;
+ char cheater[17];
+
+ fprintf( stdout, "<RD> 0000 | " );
+ j = 0;
+ for( i = 0; i < len; i++ ) {
+ cheater[j++] = isprint( buf[i] ) ? buf[i] : '.';
+ fprintf( stdout, "%02x ", buf[i] );
+
+ if( j == 16 ) {
+ cheater[j] = 0;
+ fprintf( stdout, " | %s\\n<RD> %04x | ", cheater, i+1 );
+ j = 0;
+ }
+ }
- 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, "<RD> %-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<Xapp> x;
- char* port = (char *) "4560";
- int ai = 1; // arg processing index
- cb_info_t* cbi;
- int ncb = 0; // number of callbacks registered
- int mtype;
- int nthreads = 1;
-
- cbi = (cb_info_t *) malloc( sizeof( *cbi ) );
- cbi->pcount = 0;
- cbi->icount = 0;
- cbi->stats_freq = 10;
-
- ai = 1;
- // very simple flag parsing (no error/bounds checking)
- while( ai < argc ) {
- if( argv[ai][0] != '-' ) { // break on first non-flag
- break;
- }
+ 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, "<RD> %-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<Xapp> x;
+ char* port = (char *) "4560";
+ int ai = 1; // arg processing index
+ cb_info_t* cbi;
+ int ncb = 0; // number of callbacks registered
+ int mtype;
+ int nthreads = 1;
+
+ cbi = (cb_info_t *) malloc( sizeof( *cbi ) );
+ cbi->pcount = 0;
+ cbi->icount = 0;
+ cbi->stats_freq = 10;
+
+ ai = 1;
+ // very simple flag parsing (no error/bounds checking)
+ while( ai < argc ) {
+ if( argv[ai][0] != '-' ) { // break on first non-flag
+ break;
+ }
+
+ // very simple arg parsing; each must be separate -x -y not -xy.
+ switch( argv[ai][1] ) {
+ case 'f': // enable packet forwarding
+ cbi->forward = true;
+ break;
+
+ case 'p': // define port
+ port = argv[ai+1];
+ ai++;
+ break;
+
+ case 's': // stats frequency
+ cbi->stats_freq = atoi( argv[ai+1] );
+ if( cbi->stats_freq < 5 ) { // enforce sanity
+ cbi->stats_freq = 5;
+ }
+ ai++;
+ break;
+
+ case 't': // thread count
+ nthreads = atoi( argv[ai+1] );
+ if( nthreads < 1 ) {
+ nthreads = 1;
+ }
+ ai++;
+ break;
+
+ case 'v': // simple verbose bump
+ cbi->vlevel++;
+ break;
+
+ case 'V': // explicit verbose level
+ cbi->vlevel = atoi( argv[ai+1] );
+ ai++;
+ break;
+
+ default:
+ fprintf( stderr, "unrecognised option: %s\\n", argv[ai] );
+ fprintf( stderr, "usage: %s [-f] [-p port] "
+ "[-s stats-freq] [-t thread-count] "
+ "[-v | -V n] msg-type1 ... msg-typen\\n",
+ argv[0] );
+ fprintf( stderr, "\\tstats frequency is 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, "<RD> listening on port: %s\\n", port );
+ cbi->hdr = cbi->stats_freq;
+ fprintf( stderr, "<RD> listening on port: %s\\n", port );
- // create xapp, wait for route table if forwarding
- x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
+ // create xapp, wait for route table if forwarding
+ x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
- // register callback for each type on the command line
- while( ai < argc ) {
- mtype = atoi( argv[ai] );
- ai++;
- fprintf( stderr, "<RD> capturing messages for type %d\\n", mtype );
- x->Add_msg_cb( mtype, cb1, cbi );
- ncb++;
- }
+ // register callback for each type on the command line
+ while( ai < argc ) {
+ mtype = atoi( argv[ai] );
+ ai++;
+ fprintf( stderr, "<RD> capturing messages for type %d\\n", mtype );
+ x->Add_msg_cb( mtype, cb1, cbi );
+ ncb++;
+ }
- if( ncb < 1 ) {
- fprintf( stderr, "<RD> no message types specified on the command line\\n" );
- exit( 1 );
- }
+ if( ncb < 1 ) {
+ fprintf( stderr, "<RD> no message types specified on the command line\\n" );
+ exit( 1 );
+ }
- x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi ); // register default cb
+ x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi ); // register default cb
- fprintf( stderr, "<RD> starting driver\\n" );
- x->Run( nthreads );
+ fprintf( stderr, "<RD> starting driver\\n" );
+ x->Run( nthreads );
- // return from run() is not expected, but some compilers might
- // compilain if there isn't a return value here.
- return 0;
- }
+ // 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
locked when incremented.
- ::
+ ::
- #include <stdio.h>
+ #include <stdio.h>
- #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, "<XAPP> listening on port: %s\\n", port );
- fprintf( stderr, "<XAPP> starting %d threads\\n", nthreads );
+ fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
+ fprintf( stderr, "<XAPP> 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.
to keep the example small and to the point.
- ::
+ ::
+
+ #include <stdio.h>
+ #include <string.h>
+ #include <unistd.h>
- #include <stdio.h>
- #include <string.h>
- #include <unistd.h>
+ #include <iostream>
+ #include <memory>
- #include <iostream>
- #include <memory>
+ #include "ricxfcpp/xapp.hpp"
+
+ extern int main( int argc, char** argv ) {
+ std::unique_ptr<Xapp> xfw;
+ std::unique_ptr<xapp::Message> 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, "<XAPP> response timeout set to: %d\\n", response_to );
+ fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
+
+ // get an instance and wait for a route table to be loaded
+ xfw = std::unique_ptr<Xapp>( 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, "<SNDR> 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<Xapp> xfw;
- std::unique_ptr<Message> 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 <stdio.h>
+ #include <string.h>
+ #include <unistd.h>
+
+ #include <iostream>
+ #include <memory>
+
+ #include "ricxfcpp/xapp.hpp"
+ #include "ricxfcpp/alarm.hpp"
+
+ extern int main( int argc, char** argv ) {
+ std::unique_ptr<Xapp> xfw;
+ std::unique_ptr<xapp::Message> msg;
+ xapp::Msg_component payload; // special type of unique pointer to the payload
+ std::unique_ptr<xapp::Alarm> 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, "<XAPP> response timeout set to: %d\\n", response_to );
- fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
+ fprintf( stderr, "<XAPP> response timeout set to: %d\\n", response_to );
+ fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
- // get an instance and wait for a route table to be loaded
- xfw = std::unique_ptr<Xapp>( 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<Xapp>( 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, "<SNDR> 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, "<SNDR> 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.