Add json support
[ric-plt/xapp-frame-cpp.git] / docs / user-guide.rst
index a4ab5e9..58ccd58 100644 (file)
-     
-.. 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
@@ -478,7 +474,9 @@ RMR Dump xAPP
             std::atomic<long>    icount; // messages ignored
             std::atomic<int>    hdr;    // number of messages before next header
         } cb_info_t;
+
         // ----------------------------------------------------------------------
+
         /*
             Dump bytes to tty.
         */
@@ -486,17 +484,20 @@ RMR Dump xAPP
             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-- ) {
@@ -506,13 +507,16 @@ RMR Dump xAPP
                 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() );
@@ -520,30 +524,38 @@ RMR Dump xAPP
                     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.
@@ -551,16 +563,20 @@ RMR Dump xAPP
         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";
@@ -569,25 +585,30 @@ RMR Dump xAPP
             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
@@ -595,6 +616,7 @@ RMR Dump xAPP
                         }
                         ai++;
                         break;
+
                     case 't':                        // thread count
                         nthreads = atoi( argv[ai+1] );
                         if( nthreads < 1 ) {
@@ -602,13 +624,16 @@ RMR Dump xAPP
                         }
                         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] "
@@ -620,12 +645,16 @@ RMR Dump xAPP
                                         "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] );
@@ -634,130 +663,155 @@ RMR Dump xAPP
                 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;
@@ -767,11 +821,14 @@ Looping Sender
             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)
@@ -779,10 +836,12 @@ Looping Sender
                         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;
@@ -791,25 +850,32 @@ Looping Sender
                 }
                 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 ) {
@@ -820,12 +886,12 @@ Looping Sender
                 } else {
                     msg = xfw->Alloc_msg( 2048 );
                 }
+
                 if( delay > 0 ) {
                     usleep( delay );
                 }
             }
         }
-       
-       
-      Figure 10: Simple looping sender application. 
-       
+
+      Figure 10: Simple looping sender application.
+