-
-
-.. This work is licensed under a Creative Commons Attribution 4.0 International License.
-.. SPDX-License-Identifier: CC-BY-4.0
-..
-.. CAUTION: this document is generated from source in doc/src/*
-.. To make changes edit the source and recompile the document.
-.. Do NOT make changes directly to .rst or .md files.
-
-
-============================================================================================
-RIC xAPP C++ Framework
-============================================================================================
---------------------------------------------------------------------------------------------
-User's Guide
---------------------------------------------------------------------------------------------
-
-Introduction
-============================================================================================
-
-The C++ framework allows the programmer to create an xApp
-object instance, and to use that instance as the logic base.
-The xApp object provides a message level interface to the RIC
-Message Router (RMR), including the ability to register
-callback functions which the instance will drive as messages
-are received; much in the same way that an X-windows
-application is driven by the window manager for all activity.
-The xApp may also choose to use its own send/receive loop,
-and thus is not required to use the callback driver mechanism
-provided by the framework.
-
-The Framework API
-============================================================================================
-
-The C++ framework API consists of the creation of the xApp
-object, and invoking desired functions via the instance of
-the object. The following paragraphs cover the various steps
-involved to create an xApp instance, wait for a route table
-to arrive, send a message, and wait for messages to arrive.
-
-Creating the xApp instance
---------------------------------------------------------------------------------------------
-
-The creation of the xApp instance is as simple as invoking
-the object's constructor with two required parameters:
-
-
-
- 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).
-
-
- The following code sample illustrates the simplicity of
- creating the instance of the xApp object.
-
-
- ::
-
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. SPDX-License-Identifier: CC-BY-4.0
+..
+.. CAUTION: this document is generated from source in doc/src/*
+.. To make changes edit the source and recompile the document.
+.. Do NOT make changes directly to .rst or .md files.
+
+
+============================================================================================
+User's Guide
+============================================================================================
+
+
+INTRODUCTION
+============
+
+The C++ framework allows the programmer to create an xApp
+object instance, and to use that instance as the logic base.
+The xApp object provides a message level interface to the RIC
+Message Router (RMR), including the ability to register
+callback functions which the instance will drive as messages
+are received; much in the same way that an X-windows
+application is driven by the window manager for all activity.
+The xApp may also choose to use its own send/receive loop,
+and thus is not required to use the callback driver mechanism
+provided by the framework.
+
+
+THE FRAMEWORK API
+=================
+
+The C++ framework API consists of the creation of the xApp
+object, and invoking desired functions via the instance of
+the object. The following paragraphs cover the various steps
+involved to create an xApp instance, wait for a route table
+to arrive, send a message, and wait for messages to arrive.
+
+
+Creating the xApp instance
+--------------------------
+
+The creation of the xApp instance is as simple as invoking
+the object's constructor with two required parameters:
+
+
+ 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).
+
+ The following code sample illustrates the simplicity of
+ creating the instance of the xApp object.
+
+
+ ::
+
#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.
-
- From a compilation perspective, the following is the simple
- compiler invocation string needed to compile and link the
- above program (assuming that the sample code exists in a file
- called man_ex1.cpp.
-
-
- ::
-
+
+ Figure 1: Creating an xAPP instance.
+
+ From a compilation perspective, the following is the simple
+ compiler invocation string needed to compile and link the
+ above program (assuming that the sample code exists in a file
+ called man_ex1.cpp.
+
+
+ ::
+
g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread
-
-
-
- The above program, while complete and capable of being
- compiled, does nothing useful. When invoked, RMR will be
- initialized and will begin listening for a route table;
- blocking the return to the main program until one is
- received. When a valid route table arrives, initialization
- will complete and the program will exit as there is no code
- following the instruction to create the object.
-
-Listening For Messages
-============================================================================================
-
- The program in the previous example can be extended with just
- a few lines of code to enable it to receive and process
- messages. The application needs to register a callback
- function for each message type which it desires to process.
-
- Once registered, each time a message is received the
- registered callback for the message type will be invoked by
- the framework.
-
-Callback Signature
---------------------------------------------------------------------------------------------
-
- As with most callback related systems, a callback must have a
- well known function signature which generally passes event
- related information and a "user" data pointer which was
- registered with the function. The following is the prototype
- which callback functions must be defined with:
-
-
- ::
-
+
+
+ The above program, while complete and capable of being
+ compiled, does nothing useful. When invoked, RMR will be
+ initialized and will begin listening for a route table;
+ blocking the return to the main program until one is
+ received. When a valid route table arrives, initialization
+ will complete and the program will exit as there is no code
+ following the instruction to create the object.
+
+
+LISTENING FOR MESSAGES
+======================
+
+ The program in the previous example can be extended with just
+ a few lines of code to enable it to receive and process
+ messages. The application needs to register a callback
+ function for each message type which it desires to process.
+
+ Once registered, each time a message is received the
+ registered callback for the message type will be invoked by
+ the framework.
+
+
+Callback Signature
+------------------
+
+ As with most callback related systems, a callback must have a
+ well known function signature which generally passes event
+ related information and a "user" data pointer which was
+ registered with the function. The following is the prototype
+ 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 );
-
-
- Figure 2: Callback function signature
-
- The parameters passed to the callback function are as
- follows: &multi_space
-
-
- 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).
-
-
- subid
-
- The subscription ID from the message.
-
-
- 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.)
-
-
- user data
-
- A pointer to user data. This is the pointer that was
- provided when the function was registered.
-
-
- To illustrate the use of a callback function, the previous
- code example has been extended to add the function, register
- it for message types 1000 and 1001, and to invoke the Run()
- function in the framework (explained in the next section).
-
- ::
-
+
+ Figure 2: Callback function signature
+
+ The parameters passed to the callback function are as
+ follows: &multi_space
+
+ 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).
+
+ subid
+ The subscription ID from the message.
+
+ 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.)
+
+ user data
+ A pointer to user data. This is the pointer that was
+ provided when the function was registered.
+
+ To illustrate the use of a callback function, the previous
+ code example has been extended to add the function, register
+ it for message types 1000 and 1001, and to invoke the Run()
+ function in the framework (explained in the next section).
+
+ ::
+
#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;
+
if( (counter = (long *) data) != NULL ) {
(*counter)++;
}
}
+
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 ) );
+
// 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
}
-
-
- Figure 3: Callback function example.
-
- As before, the program does nothing useful, but now it will
- execute and receive messages. For this example, the same
- function can be used to increment the appropriate counter
- simply by providing a pointer to the counter as the user data
- when the callback function is registered. In addition, a
- subtle change from the previous example has been to set the
- wait for table flag to false.
-
- For an xApp that is a receive only application (never sends)
- it is not necessary to wait for RMR to receive a table from
- the Route Manager.
-
-Registering A Default Callback
---------------------------------------------------------------------------------------------
-
- The xApp may also register a default callback function such
- that the function will be invoked for any message that does
- not have a registered callback. If the xAPP does not register
- a default callback, any message which cannot be mapped to a
- known callback function is silently dropped. A default
- callback is registered by providing a *generic* message type
- of xapp->DEFAULT_CALLBACK on an Add_msg_cb call.
-
-The Framework Callback Driver
---------------------------------------------------------------------------------------------
-
- The Run() function within the Xapp object is invoked to start
- the callback driver, and the xApp should not expect the
- function to return under most circumstances. The only
- parameter that the Run() function expects is the number of
- threads to start. For each thread requested, the framework
- will start a listener thread which will allow received
- messages to be processed in parallel. If supplying a value
- greater than one, the xApp must ensure that the callback
- functions are thread safe as it is very likely that the same
- callback function will be invoked concurrently from multiple
- threads.
-
-Sending Messages
-============================================================================================
-
- It is very likely that most xApps will need to send messages
- and will not operate in "receive only" mode. Sending the
- message is a function of the message object itself and can
- take one of two forms:
-
-
-
- $1 Replying to the sender of a received message
-
- $1 Sending a message (routed based on the message type and subscription ID)
-
-
- When replying to the sender, the message type and
- subscription ID are not used to determine the destination of
- the message; RMR ensures that the message is sent back to the
- originating xApp. The xApp may still need to change the
- message type and/or the subscription ID in the message prior
- to using the reply function.
-
- To provide for both situations, two reply functions are
- supported by the Message object as illustrated with the
- following prototypes.
-
-
- ::
-
+
+ Figure 3: Callback function example.
+
+ As before, the program does nothing useful, but now it will
+ execute and receive messages. For this example, the same
+ function can be used to increment the appropriate counter
+ simply by providing a pointer to the counter as the user data
+ when the callback function is registered. In addition, a
+ subtle change from the previous example has been to set the
+ wait for table flag to false.
+
+ For an xApp that is a receive only application (never sends)
+ it is not necessary to wait for RMR to receive a table from
+ the Route Manager.
+
+
+Registering A Default Callback
+------------------------------
+
+ The xApp may also register a default callback function such
+ that the function will be invoked for any message that does
+ not have a registered callback. If the xAPP does not register
+ a default callback, any message which cannot be mapped to a
+ known callback function is silently dropped. A default
+ callback is registered by providing a *generic* message type
+ of xapp->DEFAULT_CALLBACK on an Add_msg_cb call.
+
+
+The Framework Callback Driver
+-----------------------------
+
+ The Run() function within the Xapp object is invoked to start
+ the callback driver, and the xApp should not expect the
+ function to return under most circumstances. The only
+ parameter that the Run() function expects is the number of
+ threads to start. For each thread requested, the framework
+ will start a listener thread which will allow received
+ messages to be processed in parallel. If supplying a value
+ greater than one, the xApp must ensure that the callback
+ functions are thread safe as it is very likely that the same
+ callback function will be invoked concurrently from multiple
+ threads.
+
+
+SENDING MESSAGES
+================
+
+ It is very likely that most xApps will need to send messages
+ and will not operate in "receive only" mode. Sending the
+ message is a function of the message object itself and can
+ take one of two forms:
+
+
+ + Replying to the sender of a received message
+
+ + Sending a message (routed based on the message type and subscription ID)
+
+
+ When replying to the sender, the message type and
+ subscription ID are not used to determine the destination of
+ the message; RMR ensures that the message is sent back to the
+ originating xApp. The xApp may still need to change the
+ message type and/or the subscription ID in the message prior
+ to using the reply function.
+
+ To provide for both situations, two reply functions are
+ supported by the Message object as illustrated with the
+ following prototypes.
+
+
+ ::
+
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 );
-
-
- Figure 4: Reply function prototypes.
-
- In the first prototype the xApp must supply the new message
- type and subscription ID values, where the second function
- uses the values which are currently set in the message.
- Further, the new payload contents, and length, are supplied
- to both functions; the framework ensures that the message is
- large enough to accommodate the payload, reallocating it if
- necessary, and copies the response into the message payload
- prior to sending. Should the xApp need to change either the
- message type, or the subscription ID, but not both, the
- NO_CHANGE constant can be used as illustrated below.
-
-
- ::
-
+
+ Figure 4: Reply function prototypes.
+
+ In the first prototype the xApp must supply the new message
+ type and subscription ID values, where the second function
+ uses the values which are currently set in the message.
+ Further, the new payload contents, and length, are supplied
+ to both functions; the framework ensures that the message is
+ large enough to accommodate the payload, reallocating it if
+ necessary, and copies the response into the message payload
+ prior to sending. Should the xApp need to change either the
+ message type, or the subscription ID, but not both, the
+ NO_CHANGE constant can be used as illustrated below.
+
+
+ ::
+
msg->Send_response( Message::NO_CHANGE, Message::NO_SUBID,
pl_length, (unsigned char *) payload );
-
-
- Figure 5: Send response prototype.
-
- In addition to the two function prototypes for
- Send_response() there are two additional prototypes which
- allow the new payload to be supplied as a shared smart
- pointer. The other parameters to these functions are
- identical to those illustrated above, and thus are not
- presented here.
-
- The Send_msg() set of functions supported by the Message
- object are identical to the Send_response() functions and are
- shown below.
-
-
- ::
-
+
+ Figure 5: Send response prototype.
+
+ In addition to the two function prototypes for
+ Send_response() there are two additional prototypes which
+ allow the new payload to be supplied as a shared smart
+ pointer. The other parameters to these functions are
+ identical to those illustrated above, and thus are not
+ presented here.
+
+ The Send_msg() set of functions supported by the Message
+ object are identical to the Send_response() functions and are
+ shown below.
+
+
+ ::
+
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 payload_len,
std::shared_ptr<unsigned char> payload );
+
bool Send_msg( int payload_len, unsigned char* payload );
-
-
- Figure 6: Send function prototypes.
-
- Each send function accepts the message, copies in the payload
- provided, sets the message type and subscription ID (if
- provided), and then causes the message to be sent. The only
- difference between the Send_msg() and Send_response()
- functions is that the destination of the message is selected
- based on the mapping of the message type and subscription ID
- using the current routing table known to RMR.
-
-Direct Payload Manipulation
---------------------------------------------------------------------------------------------
-
- For some applications, it might be more efficient to
- manipulate the payload portion of an Xapp Message in place,
- rather than creating it and relying on a buffer copy when the
- message is finally sent. To achieve this, the xApp must
- either use the smart pointer to the payload passed to the
- callback function, or retrieve one from the message using
- Get_payload() when working with a message outside of a
- callback function. Once the smart pointer is obtained, the
- pointer's get() function can be used to directly reference
- the payload (unsigned char) bytes.
-
- When working directly with the payload, the xApp must take
- care not to write more than the actual payload size which can
- be extracted from the Message object using the
- Get_available_size() function.
-
- When sending a message where the payload has been directly
- altered, and no extra buffer copy is needed, a NULL pointer
- should be passed to the Message send function. The following
- illustrates how the payload can be directly manipulated and
- returned to the sender (for simplicity, there is no error
- handling if the payload size of the received message isn't
- large enough for the response string, the response is just
- not sent).
-
-
- ::
-
+
+ Figure 6: Send function prototypes.
+
+ Each send function accepts the message, copies in the payload
+ provided, sets the message type and subscription ID (if
+ provided), and then causes the message to be sent. The only
+ difference between the Send_msg() and Send_response()
+ functions is that the destination of the message is selected
+ based on the mapping of the message type and subscription ID
+ using the current routing table known to RMR.
+
+
+Direct Payload Manipulation
+---------------------------
+
+ For some applications, it might be more efficient to
+ manipulate the payload portion of an Xapp Message in place,
+ rather than creating it and relying on a buffer copy when the
+ message is finally sent. To achieve this, the xApp must
+ either use the smart pointer to the payload passed to the
+ callback function, or retrieve one from the message using
+ Get_payload() when working with a message outside of a
+ callback function. Once the smart pointer is obtained, the
+ pointer's get() function can be used to directly reference
+ the payload (unsigned char) bytes.
+
+ When working directly with the payload, the xApp must take
+ care not to write more than the actual payload size which can
+ be extracted from the Message object using the
+ Get_available_size() function.
+
+ When sending a message where the payload has been directly
+ altered, and no extra buffer copy is needed, a NULL pointer
+ should be passed to the Message send function. The following
+ illustrates how the payload can be directly manipulated and
+ returned to the sender (for simplicity, there is no error
+ handling if the payload size of the received message isn't
+ large enough for the response string, the response is just
+ not sent).
+
+
+ ::
+
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 );
}
-
-
- Figure 7: Send message without buffer copy.
-
-
-Sending Multiple Responses
---------------------------------------------------------------------------------------------
-
- It is likely that the xApp will wish to send multiple
- responses back to the process that sent a message that
- triggered the callback. The callback function may invoke the
- Send_response() function multiple times before returning.
-
- After each call, the Message retains the necessary
- information to allow for a subsequent invocation to send more
- data. It should be noted though, that after the first call to
- {Send_response() the original payload will be lost; if
- necessary, the xApp must make a copy of the payload before
- the first response call is made.
-
-Message Allocation
---------------------------------------------------------------------------------------------
-
- Not all xApps will be "responders," meaning that some xApps
- will need to send one or more messages before they can expect
- to receive any messages back. To accomplish this, the xApp
- must first allocate a message buffer, optionally initialising
- the payload, and then using the message's Send_msg() function
- to send a message out. The framework's Alloc_msg() function
- can be used to create a Message object with a desired payload
- size.
-
-Framework Provided Callbacks
-============================================================================================
-
- The framework itself may provide message handling via the
- driver such that the xApp might not need to implement some
- message processing functionality. Initially, the C++
- framework will provide a default callback function to handle
- the RMR based health check messages. This callback function
- will assume that if the message was received, and the
- callback invoked, that all is well and will reply with an OK
- state. If the xApp should need to override this simplistic
- response, all it needs to do is to register its own callback
- function for the health check message type.
-
-Example Programmes
-============================================================================================
-
- The following sections contain several example programmes
- which are written on top of the C++ framework.
-
-RMR Dump xAPP
---------------------------------------------------------------------------------------------
-
- The RMR dump application is an example built on top of the
- C++ xApp framework to both illustrate the use of the
- framework, and to provide a useful diagnostic tool when
- testing and troubleshooting xApps.
-
- The RMR dump xApp isn't a traditional xApp inasmuch as its
- goal is to listen for message types and to dump information
- about the messages received to the TTY much as tcpdump does
- for raw packet traffic. The full source code, and Makefile,
- are in the examples directory of the C++ framework repo.
-
- When invoked, the RMR dump program is given one or more
- message types to listen for. A callback function is
- registered for each, and the framework Run() function is
- invoked to drive the process. For each recognised message,
- and depending on the verbosity level supplied at program
- start, information about the received message(s) is written
- to the TTY. If the forwarding option, -f, is given on the
- command line, and an appropriate route table is provided,
- each received message is forwarded without change. This
- allows for the insertion of the RMR dump program into a flow,
- however if the ultimate receiver of a message needs to reply
- to that message, the reply will not reach the original
- sender, so RMR dump is not a complete "middle box"
- application.
-
- The following is the code for this xAPP. Several functions,
- which provide logic unrelated to the framework, have been
- omitted. The full code is in the framework repository.
-
-
-
- ::
-
+
+ Figure 7: Send message without buffer copy.
+
+
+
+Sending Multiple Responses
+--------------------------
+
+ It is likely that the xApp will wish to send multiple
+ responses back to the process that sent a message that
+ triggered the callback. The callback function may invoke the
+ Send_response() function multiple times before returning.
+
+ After each call, the Message retains the necessary
+ information to allow for a subsequent invocation to send more
+ data. It should be noted though, that after the first call to
+ {Send_response() the original payload will be lost; if
+ necessary, the xApp must make a copy of the payload before
+ the first response call is made.
+
+
+Message Allocation
+------------------
+
+ Not all xApps will be "responders," meaning that some xApps
+ will need to send one or more messages before they can expect
+ to receive any messages back. To accomplish this, the xApp
+ must first allocate a message buffer, optionally initialising
+ the payload, and then using the message's Send_msg() function
+ to send a message out. The framework's Alloc_msg() function
+ can be used to create a Message object with a desired payload
+ size.
+
+
+FRAMEWORK PROVIDED CALLBACKS
+============================
+
+ The framework itself may provide message handling via the
+ driver such that the xApp might not need to implement some
+ message processing functionality. Initially, the C++
+ framework will provide a default callback function to handle
+ the RMR based health check messages. This callback function
+ will assume that if the message was received, and the
+ callback invoked, that all is well and will reply with an OK
+ state. If the xApp should need to override this simplistic
+ response, all it needs to do is to register its own callback
+ function for the health check message type.
+
+
+EXAMPLE PROGRAMMES
+==================
+
+ The following sections contain several example programmes
+ which are written on top of the C++ framework.
+
+
+RMR Dump xAPP
+-------------
+
+ The RMR dump application is an example built on top of the
+ C++ xApp framework to both illustrate the use of the
+ framework, and to provide a useful diagnostic tool when
+ testing and troubleshooting xApps.
+
+ The RMR dump xApp isn't a traditional xApp inasmuch as its
+ goal is to listen for message types and to dump information
+ about the messages received to the TTY much as tcpdump does
+ for raw packet traffic. The full source code, and Makefile,
+ are in the examples directory of the C++ framework repo.
+
+ When invoked, the RMR dump program is given one or more
+ message types to listen for. A callback function is
+ registered for each, and the framework Run() function is
+ invoked to drive the process. For each recognised message,
+ and depending on the verbosity level supplied at program
+ start, information about the received message(s) is written
+ to the TTY. If the forwarding option, -f, is given on the
+ command line, and an appropriate route table is provided,
+ each received message is forwarded without change. This
+ allows for the insertion of the RMR dump program into a flow,
+ however if the ultimate receiver of a message needs to reply
+ to that message, the reply will not reach the original
+ sender, so RMR dump is not a complete "middle box"
+ application.
+
+ The following is the code for this xAPP. Several functions,
+ which provide logic unrelated to the framework, have been
+ omitted. The full code is in the framework repository.
+
+
+
+ ::
+
#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
std::atomic<long> icount; // messages ignored
std::atomic<int> hdr; // number of messages before next header
} cb_info_t;
+
// ----------------------------------------------------------------------
+
/*
Dump bytes to tty.
*/
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, " | %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() );
fprintf( stdout, "\\n %5s %5s %2s %5s\\n",
"MTYPE", "SUBID", "ST", "PLLEN" );
}
+
cbi.hdr = cbi.stats_freq; // reset must be last
}
}
+
void cb1( Message& mbuf, int mtype, int subid, int len,
Msg_component payload, void* data ) {
cb_info_t* cbi;
long total_count;
+
if( (cbi = (cb_info_t *) data) == NULL ) {
return;
}
+
cbi->pcount++;
stats( *cbi ); // gen stats & header if needed
+
if( cbi->vlevel > 0 ) {
fprintf( stdout, "<RD> %-5d %-5d %02d %-5d \\n",
mtype, subid, mbuf.Get_state(), len );
+
if( cbi->vlevel > 1 ) {
dump( payload.get(), len > 64 ? 64 : len );
}
}
+
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->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 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
}
ai++;
break;
+
case 't': // thread count
nthreads = atoi( argv[ai+1] );
if( 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] "
"1 message info 2 payload dump\\n" );
exit( 1 );
}
+
ai++;
}
+
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 ) );
+
// register callback for each type on the command line
while( ai < argc ) {
mtype = atoi( argv[ai] );
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 );
}
+
x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi ); // register default cb
+
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;
}
-
-
- Figure 8: Simple callback application.
-
-Callback Receiver
---------------------------------------------------------------------------------------------
-
- This sample programme implements a simple message listener
- which registers three callback functions to process two
- specific message types and a default callback to handle
- unrecognised messages.
-
- When a message of type 1 is received, it will send two
- response messages back to the sender. Two messages are sent
- in order to illustrate that it is possible to send multiple
- responses using the same received message.
-
- The programme illustrates how multiple listening threads can
- be used, but the programme is **not** thread safe; to keep
- this example as simple as possible, the counters are not
- locked when incremented.
-
-
- ::
-
+
+ Figure 8: Simple callback application.
+
+
+Callback Receiver
+-----------------
+
+ This sample programme implements a simple message listener
+ which registers three callback functions to process two
+ specific message types and a default callback to handle
+ unrecognised messages.
+
+ When a message of type 1 is received, it will send two
+ response messages back to the sender. Two messages are sent
+ in order to illustrate that it is possible to send multiple
+ responses using the same received message.
+
+ The programme illustrates how multiple listening threads can
+ be used, but the programme is **not** thread safe; to keep
+ this example as simple as possible, the counters are not
+ locked when incremented.
+
+
+ ::
+
#include <stdio.h>
+
#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;
+
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;
+
// 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,
+ 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,
+ 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;
}
+
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 );
+
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
}
-
-
- Figure 9: Simple callback application.
-
-
-Looping Sender
---------------------------------------------------------------------------------------------
-
- This is another very simple application which demonstrates
- how an application can control its own listen loop while
- sending messages. As with the other examples, some error
- checking is skipped, and short cuts have been made in order
- to keep the example small and to the point.
-
-
- ::
-
+
+ Figure 9: Simple callback application.
+
+
+
+Looping Sender
+--------------
+
+ This is another very simple application which demonstrates
+ how an application can control its own listen loop while
+ sending messages. As with the other examples, some error
+ checking is skipped, and short cuts have been made in order
+ to keep the example small and to the point.
+
+
+ ::
+
+
#include <stdio.h>
#include <string.h>
#include <unistd.h>
+
#include <iostream>
#include <memory>
+
#include "ricxfcpp/xapp.hpp"
+
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
+
int sz;
int len;
int i;
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)
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++;
}
+
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, 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 ) {
} else {
msg = xfw->Alloc_msg( 2048 );
}
+
if( delay > 0 ) {
usleep( delay );
}
}
}
-
-
- Figure 10: Simple looping sender application.
-
+
+ Figure 10: Simple looping sender application.
+