Initial source commit 78/2878/6
authorE. Scott Daniels <daniels@research.att.com>
Thu, 19 Mar 2020 15:36:37 +0000 (11:36 -0400)
committerE. Scott Daniels <daniels@research.att.com>
Thu, 19 Mar 2020 20:52:16 +0000 (16:52 -0400)
This change contains the initial source for the framework.
This is a work in progress, but should compile and produce
both .deb and .rpm packages (with proper system support).

Currently the support is for RMR based messaging only.

The examples are rough but provide a first cut demonstration
of the base framework.

Issue-ID: RIC-148

Signed-off-by: E. Scott Daniels <daniels@research.att.com>
Change-Id: I9c64aa7db20c2977a4422d7257077e2531cc67d5

22 files changed:
.gitignore [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
README
examples/Makefile [new file with mode: 0644]
examples/README [new file with mode: 0644]
examples/xapp_t1.cpp [new file with mode: 0644]
examples/xapp_t2.cpp [new file with mode: 0644]
src/messaging/CMakeLists.txt [new file with mode: 0644]
src/messaging/README [new file with mode: 0644]
src/messaging/callback.cpp [new file with mode: 0644]
src/messaging/callback.hpp [new file with mode: 0644]
src/messaging/default_cb.cpp [new file with mode: 0644]
src/messaging/default_cb.hpp [new file with mode: 0644]
src/messaging/message.cpp [new file with mode: 0644]
src/messaging/message.hpp [new file with mode: 0644]
src/messaging/messenger.cpp [new file with mode: 0644]
src/messaging/messenger.hpp [new file with mode: 0644]
src/messaging/msg_component.hpp [new file with mode: 0644]
src/xapp/CMakeLists.txt [new file with mode: 0644]
src/xapp/README [new file with mode: 0644]
src/xapp/xapp.cpp [new file with mode: 0644]
src/xapp/xapp.hpp [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..34d091a
--- /dev/null
@@ -0,0 +1,5 @@
+*.o
+*.a
+*.so
+*.sp
+.build/
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..d702d45
--- /dev/null
@@ -0,0 +1,256 @@
+#==================================================================================
+#      Copyright (c) 2019 Nokia
+#      Copyright (c) 2018-2019 AT&T Intellectual Property.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#==================================================================================
+#
+
+# This CMake definition supports several -D command line options:
+#
+#      -DDEBUG=n                       Enable debugging level n
+#      -DDEV_PKG=1                     Development package configuration
+#      -DBUILD_DOC=1           Man pages generated
+#      -DPRESERVE_PTYPE=1      Do not change the processor type when naming deb packages
+#      -DGPROF=1                       Enable profiling compile time flags
+#      -DMAN_PREFIX=<path>     Supply a path where man pages are installed (default: /usr/share/man)
+
+project( ricxfcpp )
+cmake_minimum_required( VERSION 3.5 )
+
+set( major_version "0" )               # should be automatically populated from git tag later, but until CI process sets a tag we use this
+set( minor_version "1" )
+set( patch_level "0" )
+
+set( install_root "${CMAKE_INSTALL_PREFIX}" )
+set( install_inc "include/ricxfcpp" )
+if( MAN_PREFIX )
+       set( install_man ${MAN_PREFIX} )                        # is there a cmake var for this -- can't find one
+else()
+       set( install_man "/usr/share/man" )                     # this needs to be fixed so it's not hard coded
+endif()
+
+# Must use GNUInstallDirs to install libraries into correct
+# locations on all platforms.
+include( GNUInstallDirs )
+
+# externals may install using LIBDIR as established by the gnu include; it varies from system
+# to system, and we don't trust that it is always set, so we default to lib if it is missing.
+#
+if( NOT CMAKE_INSTALL_LIBDIR )
+       set( CMAKE_INSTALL_LIBDIR "lib" )
+endif()
+
+set( install_lib "${CMAKE_INSTALL_LIBDIR}" )
+message( "+++ ricxfcpp library install target directory: ${install_lib}" )
+
+# ---------------- extract some things from git ------------------------------
+
+# commit id for the version string
+execute_process(
+       COMMAND bash -c "git rev-parse --short HEAD|awk '{printf\"%s\", $0}'"
+       OUTPUT_VARIABLE git_id
+)
+
+# version information for library names and version string
+execute_process(
+       COMMAND bash -c "git describe --tags --abbrev=0 HEAD 2>/dev/null | awk -v tag=0.0.4095 ' { tag=$1 } END{ print  tag suffix }'|sed 's/\\./;/g' "
+       OUTPUT_VARIABLE mmp_version_str
+       ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+message( "+++ mmp version from tag: '${mmp_version_str}'" )
+
+# extra indicator to show that the build was based on modified file(s) and not the true commit
+# (no hope of reproducing the exact library for debugging). Used only for the internal version
+# string.
+execute_process(
+       COMMAND bash -c "git diff --shortstat|awk -v fmt=%s -v r=-rotten '{ s=r } END { printf( fmt, s ) }'"
+       OUTPUT_VARIABLE spoiled_str
+)
+
+# uncomment these lines once CI starts adding a tag on merge
+#set( mmp_version ${mmp_version_str} )
+#list( GET mmp_version 0 major_version )
+#list( GET mmp_version 1 minor_version )
+#list( GET mmp_version 2 patch_level )
+
+if( DEBUG )                                    # if set, we'll set debugging on in the compile
+       set( debugging ${DEBUG} )
+       message( "+++ debugging is being set to ${DEBUG}" )
+else()
+       set( debugging 0 )
+       message( "+++ debugging is set to off" )
+endif()
+unset( DEBUG CACHE )                                   # we don't want this to persist
+
+
+# define constants used in the version string, debugging, etc.
+add_definitions(
+       -DGIT_ID=${git_id}
+       -DMAJOR_VER=${major_version}
+       -DMINOR_VER=${minor_version}
+       -DPATCH_VER=${patch_level}
+       -DDEBUG=${debugging}
+)
+
+# ---------------- suss out pkg gen tools so we don't fail generating packages that the system cannot support --------------
+
+# deb packages use underbars, and package manager(s) seem to flip the *_64 processor type
+# to the old (non-standard) amd64 string, so we do it here for consistency. Set -DPRESERVE_PTYPE=1
+# to prevent the flip. RPM packages will always be given the system generated processor type string.
+#
+if( ${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64" )
+       if( NOT PRESERVE_PTYPE )
+               set( deb_sys_name "amd64" )
+       else()
+               set( deb_sys_name ${CMAKE_SYSTEM_PROCESSOR} )
+       endif()
+else()
+       set( deb_sys_name ${CMAKE_SYSTEM_PROCESSOR} )
+endif()
+unset( PRESERVE_PTYPE CACHE )                                  # we don't want this to persist
+
+set( rpm_sys_name ${CMAKE_SYSTEM_PROCESSOR} )
+
+if( DEV_PKG )
+       set( deb_pkg_name "ricxfcpp-dev" )
+       set( rpm_pkg_name "ricxfcpp-devel" )
+else()
+       set( deb_pkg_name "ricxfcpp" )
+       set( rpm_pkg_name "ricxfcpp" )
+endif()
+
+set( pkg_label "ricxfcpp${spoiled_str}-${major_version}.${minor_version}.${patch_level}-${sys_name}" )
+set( rpm_pkg_label "${rpm_pkg_name}${spoiled_str}-${major_version}.${minor_version}.${patch_level}-${rpm_sys_name}" )
+set( deb_pkg_label "${deb_pkg_name}${spoiled_str}_${major_version}.${minor_version}.${patch_level}_${deb_sys_name}" )
+message( "+++ pkg name: ${deb_pkg_label}.deb" )
+
+set( gen_rpm 0 )
+find_program( rpm NAMES rpmbuild )                                     # rpm package gen requires this to be installed
+if( "${rpm}" MATCHES "rpm-NOTFOUND" )                          # cannot build rpm
+       set( pkg_list "DEB" )
+       message( "### make package will generate only deb package; cannot find support to generate rpm packages" )
+else()
+       message( "+++ pkg name: ${rpm_pkg_label}.rpm" )         # debugging if we think we can gen rpm too
+       set( pkg_list "DEB;RPM" )
+       set( gen_rpm 1 )
+       message( "+++ make package will generate both deb and rpm packages" )
+endif()
+
+
+
+# this gets us round a chicken/egg problem. include files don't exist until make is run
+# but Cmake insists on having these exist when we add them to include directories to
+# enable code to find them after we build them.
+#
+include_directories( "${CMAKE_CURRENT_SOURCE_DIR}/src/messaging" )
+
+
+# Compiler flags
+#
+set( CMAKE_POSITION_INDEPENDENT_CODE ON )
+set( CMAKE_C_FLAGS "-g " )
+set( CMAKE_CPP_FLAGS "-g " )
+if( GPROF )                                    # if set, we'll set profiling flag on compiles
+       message( "+++ profiling is on" )
+       set( CMAKE_C_FLAGS "-pg " )
+       set( CMAKE_CPP_FLAGS "-pg " )
+else()
+       message( "+++ profiling is off" )
+endif()
+unset( GPROF CACHE )                                   # we don't want this to persist
+
+# Include modules
+add_subdirectory( src/messaging )
+add_subdirectory( src/xapp )
+#add_subdirectory( doc )                               # this will auto skip if {X}fm is not available
+
+
+# shared and static libraries are built from the same object files.
+#
+add_library( ricxfcpp_shared SHARED "$<TARGET_OBJECTS:message_objects>;$<TARGET_OBJECTS:xapp_objects>" )
+set_target_properties( ricxfcpp_shared
+       PROPERTIES
+       OUTPUT_NAME "ricxfcpp"
+       SOVERSION ${major_version}
+       VERSION ${major_version}.${minor_version}.${patch_level} 
+)
+target_include_directories( ricxfcpp_shared PUBLIC "src/messenger" "src/xapp" )
+
+# we only build/export the static archive (.a) if generating a dev package
+if( DEV_PKG )
+       add_library( ricxfcpp_static STATIC "$<TARGET_OBJECTS:message_objects>;$<TARGET_OBJECTS:xapp_objects>" )
+       set_target_properties( ricxfcpp_static
+               PROPERTIES
+               OUTPUT_NAME "ricxfcpp"
+               SOVERSION ${major_version}
+               VERSION ${major_version}.${minor_version}.${patch_level} 
+       )
+       target_include_directories( ricxfcpp_static PUBLIC "src/messenger" "src/xapp" )
+endif()
+
+# ------------- packaging -----------------------------------------------------
+
+# Define what should be installed, and where they should go. For dev package we install
+# only the RMr headers, man pages and archive (.a) files.  The run-time package gets just
+# the library (.so) files and nothing more.
+#
+if( DEV_PKG )
+       set( target_list "ricxfcpp_static" )
+else()
+       set( target_list "ricxfcpp_shared" )
+endif()
+
+install( TARGETS ${target_list} EXPORT LibraryConfig
+       LIBRARY  DESTINATION ${install_lib}
+       ARCHIVE  DESTINATION ${install_lib}
+       PUBLIC_HEADER DESTINATION ${install_inc}
+)
+
+unset( DEV_PKG  CACHE )                        # prevent from being a hidden setting if user redoes things
+
+IF( EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake" )
+       include( InstallRequiredSystemLibraries )
+
+       set( CPACK_DEBIAN_PACKAGE_NAME ${deb_pkg_name} )
+       set( CPACK_RPM_PACKAGE_NAME ${rpm_pkg_name} )
+
+       set( CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "/usr/local;/usr/local/bin;/usr/local/include;/usr/local/share;/usr/local/lib" )
+
+       set( CPACK_set_DESTDIR "on" )
+       set( CPACK_PACKAGING_INSTALL_PREFIX "${install_root}" )
+       set( CPACK_GENERATOR "${pkg_list}" )
+
+       set( CPACK_PACKAGE_DESCRIPTION "C++ framework for RIC xAPPs based on RMR." )
+       set( CPACK_PACKAGE_DESCRIPTION_SUMMARY "RIC xAPP C++ framework library" )
+       set( CPACK_PACKAGE_VENDOR "None" )
+       set( CPACK_PACKAGE_CONTACT "None" )
+       set( CPACK_PACKAGE_VERSION_MAJOR "${major_version}" )
+       set( CPACK_PACKAGE_VERSION_MINOR "${minor_version}" )
+       set( CPACK_PACKAGE_VERSION_PATCH "${patch_level}" )
+       set( CPACK_PACKAGE "${pkg_label}" )                                             # generic name for old versions of cpack
+       set( CPACK_DEBIAN_FILE_NAME "${deb_pkg_label}.deb" )
+       set( CPACK_RPM_FILE_NAME "${rpm_pkg_label}.rpm" )
+
+       # Future: define dependencies on RMR and other libs
+
+       set( CPACK_DEBIAN_PACKAGE_PRIORITY "optional" )
+       set( CPACK_DEBIAN_PACKAGE_SECTION "ric" )
+       set( CPACK_DEBIAN_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR} )
+       set( CPACK_RPM_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR} )
+
+       # this seems ingnored if included
+       #set( CPACK_COMPONENTS_ALL Libraries ApplicationData )
+
+       INCLUDE( CPack )
+ENDIF()
diff --git a/README b/README
index 58afaea..e4dc3ff 100644 (file)
--- a/README
+++ b/README
@@ -1,23 +1,22 @@
-#
-#==================================================================================
-#       Copyright (c) 2020 Nokia
-#       Copyright (c) 2020 AT&T Intellectual Property.
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-#==================================================================================
-#
 
-C++ xAPP Framework
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
 
-This repository contains a "framework" on which C++ applications
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+
+xAPP C++ Framework
+
+This repository contains a "framework" on which C++ RIC applications (xAPPs)
 can be built.
diff --git a/examples/Makefile b/examples/Makefile
new file mode 100644 (file)
index 0000000..1371871
--- /dev/null
@@ -0,0 +1,33 @@
+# vim: ts=4 sw=4 noet:
+
+#==================================================================================
+#      Copyright (c) 2020 Nokia
+#      Copyright (c) 2020 AT&T Intellectual Property.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#==================================================================================
+
+# simple makefile to build the examples. This assumes that the xapp framework
+# library has been installed or the LD_LIBRARY_PATH and C_INCLUDE_PATH environent
+# variables are set to reference the needed files.
+
+%.o:: %.cpp %.hpp
+       g++ -g ${prereq%% *} -c 
+
+all:: xapp_t1 xapp_t2
+
+xapp_t1:: xapp_t1.cpp
+       g++ xapp_t1.cpp -g -o xapp_t1 -lricxfcpp -lrmr_si -lpthread -lm
+
+xapp_t2:: xapp_t2.cpp
+       g++ xapp_t2.cpp -g -o xapp_t2 -lricxfcpp -lrmr_si -lpthread -lm
diff --git a/examples/README b/examples/README
new file mode 100644 (file)
index 0000000..70f00ff
--- /dev/null
@@ -0,0 +1,33 @@
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+
+This directory contains a few simple examples which illustrate
+the various ways that the xAPP-C++ framework (xfcpp) can be
+used.
+
+xapp_t1.cpp
+       A receiver which demonstrates how callbacks can be
+       declared and used to process specific messages.
+       Also demonstrates the simplicity of setting up an
+       xAPP which simply responds to received messages (a
+       total of 4 instuctions in main()).
+
+xapp_t2.cpp
+       A sender application which demonstrates how the framework
+       can be used for applications which are sending based
+       and need to check for responses. This does not involve
+       the use of callbacks or the frameworks "driver".
diff --git a/examples/xapp_t1.cpp b/examples/xapp_t1.cpp
new file mode 100644 (file)
index 0000000..8cce435
--- /dev/null
@@ -0,0 +1,122 @@
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+*/
+
+/*
+       Mnemonic:       xapp_t1.cpp
+       Abstract:       This is a simple demo xapp which registers 3 callback functions
+                               (2 for specific message types, and one default callback). It
+                               defaults to starting 1 thread, but can cause the xapp environment
+                               to start n listeners.  When running 1 thread it should emit 
+                               message countes ever few seconds with a crudly computed msg/sec 
+                               receive rate value.
+
+                               In addition, the callback for message type 1 will send two response
+                               messages to verify/illustrate that the same received buffer can
+                               be used to construct multiple messages and given to the return to
+                               sender function in RMR for multiple message reponse generation.
+
+       Date:           18 March 2020
+       Author:         E. Scott Daniels
+               
+*/
+#include <stdio.h>
+
+#include "ricxfcpp/message.hpp"
+#include "ricxfcpp/msg_component.hpp"
+#include "ricxfcpp/xapp.hpp"
+
+// ----------------------------------------------------------
+
+// counts/time values for crude rate estimation; only valid when threads == 1
+long cb1_count = 0;
+long cb2_count = 0;
+long cbd_count = 0;
+
+long cb1_lastts = 0;
+long cb1_lastc = 0;
+
+void cb1( Messenger& mr, Message& mbuf, int mtype, int subid, int len, Msg_component payload,  void* data ) {
+       long now;
+       long total_count;
+
+       //fprintf( stderr, "callback 1 got a message type = %d len = %d\n", mtype, len );
+       mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK1\n" );    // validate that we can use the same buffer for 2 rts calls
+       mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\n" );
+       cb1_count++;
+       
+       now = time( NULL );
+       if( now - cb1_lastts > 5 ) {                            // crude rate estimation starting with second timer pop
+               if( cb1_lastts ) {
+                       total_count = cb1_count + cb2_count;
+                       fprintf( stderr, "cb1: %ld  diff=%ld ~rate=%ld\n", total_count, now - cb1_lastts, (total_count-cb1_lastc)/(now - cb1_lastts) );
+                       cb1_lastc = total_count;
+               }
+               cb1_lastts = now;
+       }
+}
+
+void cb2( Messenger& mr, Message& mbuf, int mtype, int subid, int len, Msg_component payload,  void* data ) {
+       //fprintf( stderr, "callback 2 got a message type = %d len = %d\n", mtype, len );
+       //mbuf.Send_msg( 101, -1, 4, (unsigned char *) "HI\n" );                // send, including the trailing 0
+       cb2_count++;
+}
+
+void cbd( Messenger& mr, Message& mbuf, int mtype, int subid, int len, Msg_component payload,  void* data ) {
+       //fprintf( stderr, "default callback  got a message type = %d len = %d\n", mtype, len );
+       cbd_count++;
+}
+
+int main( int argc, char** argv ) {
+       Xapp* x;
+       char*   port = (char *) "4560";
+       int ai = 1;                                                     // arg processing index
+       int nthreads = 1;
+
+       ai = 1;
+       while( ai < argc ) {                            // very simple flag processing (no bounds/error checking)
+               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 );
+       x->Add_msg_cb( 2, cb2, NULL );
+       x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL );
+
+       x->Run( nthreads );
+}
diff --git a/examples/xapp_t2.cpp b/examples/xapp_t2.cpp
new file mode 100644 (file)
index 0000000..6270f92
--- /dev/null
@@ -0,0 +1,108 @@
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+*/
+
+/*
+       Mnemonic:       xapp_t2.cpp
+       Abstract:       This is a simple demo xapp which controls it's own listen
+                               loop (does not register callbacks and does not invoke the
+                               run function in the xapp instance.
+
+       Date:           18 March 2020
+       Author:         E. Scott Daniels
+               
+*/
+
+#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 mtype;
+       int     sz;
+       int i;
+       int ai;
+       int response_to = 0;                            // max timeout wating for a response
+       char*   port = (char *) "4555";
+       
+
+       ai = 1;
+       while( ai < argc ) {                            // very simple flag processing (no bounds/error checking)
+               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':                                       // timeout in seconds; we need to convert to ms for rmr calls
+                               response_to = atoi( argv[ai+1] ) * 1000;
+                               ai++;
+                               break;
+               }
+               ai++;
+       }
+
+       fprintf( stderr, "<XAPP> response timeout set to: %d\n", response_to );
+       fprintf( stderr, "<XAPP> listening on port: %s\n", port );
+
+       xfw = std::unique_ptr<Xapp>( new Xapp( port, true ) );          // new xAPP thing; wait for a route table
+       msg = xfw->Alloc_msg( 2048 );
+
+       for( i = 0; i < 100; i++ ) {
+               sz = msg->Get_available_size();                 // we'll reuse a message if we received one back; ensure it's big enough
+               if( sz < 2048 ) {
+                       fprintf( stderr, "<SNDR> fail: message returned did not have enough size: %d [%d]\n", sz, i );
+                       exit( 1 );
+               }
+
+               payload = msg->Get_payload();                                                                                   // direct access to payload
+               snprintf( (char *) payload.get(), 2048, "This is message %d\n", i );    // something silly to send
+
+               // payload updated in place, nothing to copy from, so payload parm is nil
+               if ( ! msg->Send_msg( 1, Message::NO_SUBID, strlen( (char *) payload.get() )+1, NULL )) {
+                       fprintf( stderr, "<SNDR> send failed: %d\n", i );
+               }
+
+               msg = xfw->Receive( response_to );
+               if( msg != NULL ) {
+                       mtype = msg->Get_mtype();
+                       payload = msg->Get_payload();
+                       fprintf( stderr, "got: mtype=%d payload=(%s)\n", mtype, (char *) payload.get() );
+               } else {
+                       msg = xfw->Alloc_msg( 2048 );                           // nothing back, need a new message to send
+               }
+
+               sleep( 1 );
+       }
+}
diff --git a/src/messaging/CMakeLists.txt b/src/messaging/CMakeLists.txt
new file mode 100644 (file)
index 0000000..507da84
--- /dev/null
@@ -0,0 +1,47 @@
+# vim: sw=4 ts=4 noet:
+#
+#==================================================================================
+#   Copyright (c) 2020 Nokia
+#   Copyright (c) 2020 AT&T Intellectual Property.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#==================================================================================
+#
+
+
+# For clarity: this generates object, not a lib as the CM command implies.
+#
+add_library( message_objects OBJECT
+       callback.cpp    
+       default_cb.cpp
+       message.cpp
+       messenger.cpp
+)
+
+target_include_directories (message_objects PUBLIC
+       $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+       $<INSTALL_INTERFACE:include>
+       PRIVATE src)
+
+# header files should go into .../include/xfcpp/
+if( DEV_PKG )
+       install( FILES
+               callback.hpp  
+               default_cb.hpp  
+               message.hpp  
+               messenger.hpp
+               msg_component.hpp
+               DESTINATION ${install_inc}
+       ) 
+endif()
+
diff --git a/src/messaging/README b/src/messaging/README
new file mode 100644 (file)
index 0000000..0683082
--- /dev/null
@@ -0,0 +1,47 @@
+
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+
+This directory contains the source for the RMR messaging portion of the 
+framework.  Specifially:
+
+       message.*
+               provides a message class from which the user can extract/set information
+               (type, subscription ID, payload, etc.) and provides instance funcitons
+               for sending and replying to the message. User programmes will interact
+               directly with this.
+
+       msg_component.*
+               This is a small type def that allows some of the getter functions in message
+               to return a smart pointer to memory which cannot directly be released (e.g
+               the payload).   This allows the user programme to declare a variable of type
+               Msg_component (which is really a smart pointer with a custom deleter) rather
+               than having to understand the needed smart pointer construct for these things.
+
+       messenger.*
+               This class provides the direct "wrapper" on top of RMR. It supports 
+               high level operations such as Listen and callback management.
+
+       callback.*
+               This provides the callback support to messenger (listen) functions.
+
+       default_cb.*
+               This module contains any default callback functions which are provided
+               by the framework (e.g. health check processing). 
+
+
+       
diff --git a/src/messaging/callback.cpp b/src/messaging/callback.cpp
new file mode 100644 (file)
index 0000000..d7eacc6
--- /dev/null
@@ -0,0 +1,62 @@
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+*/
+
+/*
+       Mnemonic:       callback.cpp
+       Abstract:       Function defs for the callback managment object
+       Date:           9 March 2020
+       Author:         E. Scott Daniels
+*/
+
+#include <cstdlib>
+
+#include <rmr/rmr.h>
+
+#include "message.hpp"
+//class Messenger;
+
+
+/*
+       Builder.
+*/
+Callback::Callback( user_callback ufun, void* data ) {         // builder
+       user_fun = ufun;
+       udata = data;
+}
+
+/*
+       Wrecking ball.
+*/
+Callback::~Callback() {
+       // nothing to do at the moment.
+}
+
+/*
+       Drive_cb will invoke the callback and pass along the stuff passed here.
+*/
+void Callback::Drive_cb( Messenger& mr, Message& m ) {
+       if( user_fun == NULL ) {
+               return;
+       }
+
+       user_fun( mr, m, m.Get_mtype(), m.Get_subid(), m.Get_len(), m.Get_payload(),  udata );
+}
+
+
diff --git a/src/messaging/callback.hpp b/src/messaging/callback.hpp
new file mode 100644 (file)
index 0000000..2b7813d
--- /dev/null
@@ -0,0 +1,54 @@
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+*/
+
+/*
+       Mnemonic:       callback.hpp
+       Abstract:       class description for a callback managment object.
+       Date:           9 March 2020
+       Author:         E. Scott Daniels
+*/
+
+
+#ifndef _CALLBACK_HPP
+#define _CALLBACK_HPP
+
+#include <memory>
+
+class Messenger;
+class Message;
+#include "message.hpp"
+
+typedef void(*user_callback)( Messenger& mr, Message& m, int mtype, int subid, int payload_len, Msg_component payload, void* usr_data );
+                                                               //std::unique_ptr<unsigned char,unfreeable> payload, void* usr_data );
+
+class Callback {
+
+       private:
+               user_callback user_fun;
+               void*   udata;                                                                  // user data
+
+       public:
+               Callback( user_callback, void* data );                                  // builder
+               ~Callback();                                                                                    // destroyer
+               void Drive_cb( Messenger& mr, Message& m );             // invoker
+};
+
+
+#endif
diff --git a/src/messaging/default_cb.cpp b/src/messaging/default_cb.cpp
new file mode 100644 (file)
index 0000000..d7cfdbf
--- /dev/null
@@ -0,0 +1,54 @@
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+*/
+
+/*
+       Mnemonic:       default_cb.cpp
+       Abstract:       This is a set of static functions that are used as
+                               the default Messenger callbacks.
+
+       Date:           11 Mar 2020
+       Author:         E. Scott Daniels
+*/
+
+#include <string.h>
+#include <unistd.h>
+
+#include <rmr/rmr.h>
+#include <rmr/RIC_message_types.h>
+
+
+#include <cstdlib>
+
+#include "messenger.hpp"
+
+/*
+       This is the default health check function that we provide (user
+       may override it).  It will respond to health check messages by
+       sending an OK back to the source.
+
+       The mr paramter is obviously ignored, but to add this as a callback
+       the function sig must match.
+*/
+void Health_ck_cb( Messenger& mr, Message& mbuf, int mtype, int sid, int len, Msg_component payload, void* data ) {
+       unsigned char response[128];
+
+       snprintf( (char* ) response, sizeof( response ), "OK\n" );
+       mbuf.Send_response( RIC_HEALTH_CHECK_RESP, sid, strlen( (char *) response )+1, response );
+}
diff --git a/src/messaging/default_cb.hpp b/src/messaging/default_cb.hpp
new file mode 100644 (file)
index 0000000..fdc084a
--- /dev/null
@@ -0,0 +1,39 @@
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+*/
+
+/*
+       Mnemonic:       default_cb.hpp
+       Abstract:       Headers/prototypes for the default callbacks. The
+                               default callbacks are those which we install when
+                               the messenger is created and handles things that
+                               the application might not want to (e.g. health check).
+
+       Date:           11 March 2020
+       Author:         E. Scott Daniels
+*/
+
+#ifndef _DEF_CB_H
+#define _DEF_CB_H
+
+
+void Health_ck_cb( Messenger& mr, Message& mbuf, int mtype, int sid, int len, Msg_component payload, void* data );
+
+
+#endif
diff --git a/src/messaging/message.cpp b/src/messaging/message.cpp
new file mode 100644 (file)
index 0000000..0c6b262
--- /dev/null
@@ -0,0 +1,320 @@
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+*/
+
+/*
+       Mnemonic:       message.cpp
+       Abstract:       A message wrapper. This should completely hide the
+                               underlying transport (RMR) message structure from
+                               the user application. For the most part, the getters
+                               are used by the framwork; it is unlikely that other
+                               than adding/extracting the MEID, the user app will
+                               be completely unaware of information that is not
+                               presented in the callback parms.
+
+       Date:           12 March 2020
+       Author:         E. Scott Daniels
+*/
+
+#include <string.h>
+#include <unistd.h>
+
+#include <rmr/rmr.h>
+
+#include <iostream>
+
+#include "message.hpp"
+
+// --------------- private ------------------------------------------------
+
+// --------------- builders -----------------------------------------------
+
+/*
+       Create a new message wrapper for an existing RMR msg buffer.
+*/
+Message::Message( rmr_mbuf_t* mbuf, void* mrc ) {
+       this->mrc = mrc;                        // the message router context for sends
+       this->mbuf = mbuf;
+}
+
+Message::Message( void* mrc, int payload_len ) {
+       this->mrc = mrc;
+       this->mbuf = rmr_alloc_msg( mrc, payload_len );
+}
+
+/*
+       Destroyer.
+*/
+Message::~Message() {
+       if( mbuf != NULL ) {
+               rmr_free_msg( mbuf );
+       }
+
+       mbuf = NULL;
+}
+
+
+// --- getters/setters -----------------------------------------------------
+/*
+       Copy the payload bytes, and return a smart pointer (unique) to it.
+       If the application needs to update the payload in place for a return
+       to sender call, or just to access the payload in a more efficent manner
+       (without the copy), the Get_payload() function should be considered.
+
+       This function will return a NULL pointer if malloc fails.
+*/
+//char* Message::Copy_payload( ){
+std::unique_ptr<unsigned char> Message::Copy_payload( ){
+       unsigned char*  new_payload = NULL;
+
+       if( mbuf != NULL ) {
+               new_payload = (unsigned char *) malloc( sizeof( unsigned char ) * mbuf->len );
+               memcpy( new_payload, mbuf->payload, mbuf->len );
+       }
+
+       return std::unique_ptr<unsigned char>( new_payload );
+}
+
+/*
+       Makes a copy of the MEID and returns a smart pointer to it.
+*/
+std::unique_ptr<unsigned char> Message::Get_meid(){
+       unsigned char* m = NULL;
+
+       if( m != NULL ) {
+               m = (unsigned char *) malloc( sizeof( unsigned char ) * RMR_MAX_MEID );
+               rmr_get_meid( mbuf, m );
+       }
+
+       return std::unique_ptr<unsigned char>( m );
+}
+
+int Message::Get_available_size(){
+       if( mbuf != NULL ) {
+               return rmr_payload_size( mbuf );
+       }
+
+       return 0;
+}
+
+int    Message::Get_mtype(){
+       if( mbuf != NULL ) {
+               return mbuf->mtype;
+       }
+
+       return INVALID_MTYPE;
+}
+
+/*
+       Makes a copy of the source field and returns a smart pointer to it.
+*/
+std::unique_ptr<unsigned char> Message::Get_src(){
+       unsigned char* m = NULL;
+
+       m = (unsigned char *) malloc( sizeof( unsigned char ) * RMR_MAX_SRC );
+       if( m != NULL ) {
+               rmr_get_src( mbuf, m );
+       }
+
+       return std::unique_ptr<unsigned char>( m );
+}
+
+int    Message::Get_state( ){
+       if( mbuf != NULL ) {
+               return mbuf->state;
+       }
+
+       return INVALID_STATUS;
+}
+
+int    Message::Get_subid(){
+       if( mbuf != NULL ) {
+               return mbuf->sub_id;
+       }
+
+       return INVALID_SUBID;
+}
+
+int    Message::Get_len(){
+       if( mbuf != NULL ) {
+               return mbuf->len;
+       }
+
+       return 0;
+}
+
+/*
+       This returns a smart (unique) pointer to the payload portion of the
+       message. This provides the user application with the means to
+       update the payload in place to avoid multiple copies.  The
+       user programme is responsible to determing the usable payload
+       length by calling Message:Get_available_size(), and ensuring that
+       writing beyond the indicated size does not happen.
+*/
+Msg_component Message::Get_payload(){
+       if( mbuf != NULL ) {
+               return std::unique_ptr<unsigned char, unfreeable>( mbuf->payload );
+       }
+
+       return NULL;
+}
+
+void Message::Set_meid( std::unique_ptr<unsigned char> new_meid ) {
+       if( mbuf != NULL ) {
+               rmr_str2meid( mbuf, (unsigned char *) new_meid.get() );
+       }
+}
+
+void Message::Set_mtype( int new_type ){
+       if( mbuf != NULL ) {
+               mbuf->mtype = new_type;
+       }
+}
+
+void Message::Set_subid( int new_subid ){
+       if( mbuf != NULL ) {
+               mbuf->sub_id = new_subid;
+       }
+}
+
+
+// -------------- send functions ---------------------------------
+
+/*
+       This assumes that the contents of the mbuf were set by either a send attempt that
+       failed with a retry and thus is ready to be processed by RMR.
+       Exposed to the user, but not expected to be frequently used.
+*/
+bool Message::Send( ) {
+       if( mbuf == NULL ) {
+               return false;
+       }
+
+       mbuf =  rmr_send_msg( mrc, mbuf );
+       return mbuf->state == RMR_OK;
+}
+
+/*
+       Similar to Send(), this assumes that the message is already set up and this is a retry.
+       Exposed to the user, but not expected to be frequently used.
+*/
+bool Message::Reply( ) {
+       if( mbuf == NULL ) {
+               return false;
+       }
+
+       mbuf =  rmr_rts_msg( mrc, mbuf );
+       return mbuf->state == RMR_OK;
+}
+
+/*
+       Send workhorse.
+       This will setup the message (type etc.) ensure the message payload space is
+       large enough and copy in the payload (if a new payload is given), then will
+       either send or rts the message based on the stype parm.
+
+       If payload is nil, then we assume the user updated the payload in place and
+       no copy is needed.
+
+       This is public, but most users should use Send_msg or Send_response functions.
+*/
+bool Message::Send( int mtype, int subid, int payload_len, unsigned char* payload, int stype ) {
+
+       if( mbuf == NULL ) {
+               return false;
+       }
+
+       mbuf->mtype = mtype;
+       mbuf->sub_id = subid;
+       mbuf->len = payload_len;
+
+       if( payload != NULL ) {                 // if we have a payload, ensure msg has room, realloc if needed, then copy
+               mbuf = rmr_realloc_payload( mbuf, payload_len, RMR_NO_COPY, RMR_NO_CLONE );             // ensure message is large enough
+               if( mbuf == NULL ) {
+                       return false;
+               }
+
+               memcpy( mbuf->payload, payload, mbuf->len );
+       }
+
+       if( stype == RESPONSE ) {
+               mbuf = rmr_rts_msg( mrc, mbuf );
+       } else {
+               mbuf = rmr_send_msg( mrc, mbuf );
+       }
+
+       return mbuf->state == RMR_OK;
+}
+
+/*
+       Send a response to the endpoint that sent the original message.
+
+       Response can be null and the assumption will be that the message payload
+       was updated in place and no additional copy is needed before sending the message.
+
+       The second form of the call allows for a stack allocated buffer (e.g. char foo[120]) to
+       be passed as the payload.
+*/
+bool Message::Send_response(  int mtype, int subid, int response_len, std::unique_ptr<unsigned char> response ) {
+       return Send( mtype, subid, response_len, response.get(), RESPONSE );
+}
+
+bool Message::Send_response(  int mtype, int subid, int response_len, unsigned char* response ) {
+       return Send( mtype, subid, response_len, response, RESPONSE );
+}
+
+/*
+       These allow a response message to be sent without changing the mtype/subid.
+*/
+bool Message::Send_response(  int response_len, std::unique_ptr<unsigned char> response ) {
+       return Send( NOCHANGE, NOCHANGE, response_len, response.get(), RESPONSE );
+}
+
+bool Message::Send_response(  int response_len, unsigned char* response ) {
+       return Send( NOCHANGE, NOCHANGE, response_len, response, RESPONSE );
+}
+
+
+/*
+       Send a message based on message type routing.
+
+       Payload can be null and the assumption will be that the message payload
+       was updated in place and no additional copy is needed before sending the message.
+
+       Return is a new mbuf suitable for sending another message, or the original buffer with
+       a bad state sent if there was a failure.
+*/
+bool Message::Send_msg(  int mtype, int subid, int payload_len, std::unique_ptr<unsigned char> payload ) {
+       return Send( mtype, subid, payload_len, payload.get(), MESSAGE );
+}
+
+bool Message::Send_msg(  int mtype, int subid, int payload_len, unsigned char* payload ) {
+       return Send( mtype, subid, payload_len, payload, MESSAGE );
+}
+
+/*
+       Similar send functions that allow the message type/subid to remain unchanged
+*/
+bool Message::Send_msg(  int payload_len, std::unique_ptr<unsigned char> payload ) {
+       return Send( NOCHANGE, NOCHANGE, payload_len, payload.get(), MESSAGE );
+}
+
+bool Message::Send_msg(  int payload_len, unsigned char* payload ) {
+       return Send( NOCHANGE, NOCHANGE, payload_len, payload, MESSAGE );
+}
diff --git a/src/messaging/message.hpp b/src/messaging/message.hpp
new file mode 100644 (file)
index 0000000..8c06ed1
--- /dev/null
@@ -0,0 +1,106 @@
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+*/
+
+/*
+       Mnemonic:       message.hpp
+       Abstract:       Headers for the message class.
+                               This class provides an interface to the user application
+                               when information from the message is needed.
+
+       Date:           10 March 2020
+       Author:         E. Scott Daniels
+*/
+
+#ifndef _MESSAGE_HPP
+#define _MESSAGE_HPP
+
+
+#include <iostream>
+#include <string>
+
+#include <rmr/rmr.h>
+
+#include "msg_component.hpp"
+#include "callback.hpp"
+#include "default_cb.hpp"
+
+#ifndef RMR_NO_CLONE
+       #define RMR_NO_CLONE    0
+       #define RMR_NO_COPY     0
+       #define RMR_CLONE       1
+       #define RMR_COPY        1
+#endif
+
+
+// ------------------------------------------------------------------------
+
+class Message {
+       private:
+               static const int        NOCHANGE = -99; // internal constant indicating no change to something
+
+               rmr_mbuf_t*     mbuf;                                   // the underlying RMR message buffer
+               void*           mrc;                                    // message router context
+               std::shared_ptr<char> psp;                      // shared pointer to the payload to give out
+
+       public:
+               static const int        INVALID_MTYPE = -1;
+               static const int        INVALID_STATUS = -1;
+               static const int        INVALID_SUBID = -2;
+               static const int        NO_SUBID = -1;                                  // not all messages have a subid
+
+               static const int        RESPONSE = 0;                                   // send types
+               static const int        MESSAGE = 1;
+
+               Message( rmr_mbuf_t* mbuf, void* mrc );         // builders
+               Message( void* mrc, int payload_len );
+               ~Message();                                                                     // destroyer
+
+               std::unique_ptr<unsigned char>  Copy_payload( );                // copy the payload; deletable smart pointer
+
+               std::unique_ptr<unsigned char> Get_meid();              // returns a copy of the meid bytes
+               int Get_available_size();
+               int     Get_len();
+               int     Get_mtype();
+               Msg_component Get_payload();
+               std::unique_ptr<unsigned char>  Get_src();
+               int     Get_state( );
+               int     Get_subid();
+
+               void Set_meid( std::unique_ptr<unsigned char> new_meid );
+               void Set_mtype( int new_type );
+               void Set_subid( int new_subid );
+
+               bool Reply( );
+               bool Send( );
+               bool Send( int mtype, int subid, int payload_len, unsigned char* payload, int stype );
+
+               bool Send_msg( int mtype, int subid, int payload_len, std::unique_ptr<unsigned char> payload );
+               bool Send_msg( int mtype, int subid, int payload_len, unsigned char* payload );
+               bool Send_msg( int payload_len, std::unique_ptr<unsigned char> payload );
+               bool Send_msg( int payload_len, unsigned char* payload );
+
+               bool Send_response( int mtype, int subid, int payload_len, std::unique_ptr<unsigned char> response );
+               bool Send_response( int mtype, int subid, int payload_len, unsigned char* response );
+               bool Send_response( int payload_len, std::unique_ptr<unsigned char> response );
+               bool Send_response( int payload_len, unsigned char* response );
+};
+
+
+#endif
diff --git a/src/messaging/messenger.cpp b/src/messaging/messenger.cpp
new file mode 100644 (file)
index 0000000..d4033c3
--- /dev/null
@@ -0,0 +1,231 @@
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+*/
+
+/*
+       Mnemonic:       messenger.cpp
+       Abstract:       Message Router Messenger.
+
+       Date:           10 March 2020
+       Author:         E. Scott Daniels
+*/
+
+#include <string.h>
+#include <unistd.h>
+
+#include <rmr/rmr.h>
+#include <rmr/RIC_message_types.h>
+
+
+#include <iostream>
+#include <string>
+#include <map>
+#include <memory>
+#include <mutex>
+
+#include "callback.hpp"
+#include "default_cb.hpp"              // default callback prototypes
+#include "message.hpp"
+#include "messenger.hpp"
+
+
+// --------------- private -----------------------------------------------------
+
+
+// ---------------- C++ buggerd up way of maintining class constants ----------
+const int Messenger::MAX_PAYLOAD = (1024*64);
+const int Messenger::DEFAULT_CALLBACK = -1;
+
+// --------------- builders -----------------------------------------------
+/*
+       If wait4table is true, then the construction of the object does not
+       complete until the underlying transport has a new copy of the route
+       table.
+
+       If port is nil, then the default port is used (4560).
+*/
+Messenger::Messenger( char* port, bool wait4table ) {
+       if( port == NULL ) {
+               port = (char *) "4560";
+       }
+
+       gate = new std::mutex();
+       listen_port = strdup( port );
+       mrc = rmr_init( listen_port, Messenger::MAX_PAYLOAD, 0 );
+
+       if( wait4table ) {
+               this->Wait_for_cts( 0 );
+       }
+
+       Add_msg_cb( RIC_HEALTH_CHECK_REQ, Health_ck_cb, NULL );         // add our default call backs
+
+       ok_2_run = true;
+}
+
+/*
+       Destroyer.
+*/
+Messenger::~Messenger() {
+       if( mrc != NULL ) {
+               rmr_close( mrc );
+       }
+
+       free( listen_port );
+}
+
+/*
+       Allow user to register a callback function invoked when a specific type of
+       message is received.  The user may pass an optional data pointer which
+       will be passed to the function when it is called.  The function signature
+       must be:
+               void fun( Messenger* mr, rmr_mbuf_t* mbuf,  void* data );
+
+       The user can also invoke this function to set the "default" callback by
+       passing Messenger::DEFAULT_CALLBACK as the mtype. If no other callback
+       is defined for a message type, the default callback function is invoked.
+       If a default is not provided, a non-matching message is silently dropped.
+*/
+void Messenger::Add_msg_cb( int mtype, user_callback fun_name, void* data ) {
+       Callback*       cb;
+
+       cb = new Callback( fun_name, data );
+       cb_hash[mtype] = cb;
+
+       callbacks = true;
+}
+
+/*
+       Message allocation for user to send. User must destroy the message when
+       finished, but may keep the message for as long as is necessary
+       and reuse it over and over.
+*/
+//Message* Messenger::Alloc_msg( int payload_size ) {
+std::unique_ptr<Message> Messenger::Alloc_msg( int payload_size ) {
+       return std::unique_ptr<Message>( new Message( mrc, payload_size ) );
+}
+
+void Messenger::Listen( ) {
+       int count = 0;
+       rmr_mbuf_t*     mbuf = NULL;
+       std::map<int,Callback*>::iterator mi;   // map iterator; silly indirect way to point at the value
+       Callback*       dcb = NULL;                                     // default callback so we don't search
+       Callback*       sel_cb;                                         // callback selected to invoke
+       std::unique_ptr<Message>m;
+
+       if( mrc == NULL ) {
+               return;
+       }
+
+       mi = cb_hash.find( DEFAULT_CALLBACK );
+       if( mi != cb_hash.end() ) {
+               dcb = mi->second;                                       // oddly named second field is the address of the callback block
+       }
+
+       while( ok_2_run ) {
+               mbuf = rmr_torcv_msg( mrc, mbuf, 2000 );                // come up for air every 2 sec to check ok2run
+               if( mbuf != NULL ) {
+                       if( mbuf->state == RMR_OK ) {
+                               m = std::unique_ptr<Message>( new Message( mbuf, mrc ) );       // auto delteted when scope terminates
+
+                               sel_cb = dcb;                                                                                   // start with default
+                               if( callbacks  && ((mi = cb_hash.find( mbuf->mtype )) != cb_hash.end()) ) {
+                                       sel_cb = mi->second;                                                            // override with user callback
+                               }
+                               if( sel_cb != NULL ) {
+                                       sel_cb->Drive_cb( *this, *m );                                          // drive the selected one
+                                       mbuf = NULL;                                                                            // not safe to use after given to cb
+                               }
+                       } else {
+                               if( mbuf->state != RMR_ERR_TIMEOUT ) {
+                                       fprintf( stderr, "<LISTENER> got  bad status: %d\n", mbuf->state );
+                               }
+                       }
+               }
+       }
+}
+
+/*
+       Wait for the next message, up to a max timout, and return the message received.
+*/
+std::unique_ptr<Message>  Messenger::Receive( int timeout ) {
+       rmr_mbuf_t*     mbuf = NULL;
+       //std::unique_ptr<Message> m;
+
+       if( mrc == NULL ) {
+               return NULL;
+       }
+
+       mbuf = rmr_torcv_msg( mrc, mbuf, timeout );             // future: do we want to reuse the mbuf here?
+       if( mbuf != NULL ) {
+               return std::unique_ptr<Message>( new Message( mbuf, mrc ) );
+       }
+
+       return NULL;
+}
+
+/*
+       Called to gracefully stop all listeners.
+*/
+void Messenger::Stop( ) {
+       ok_2_run = false;
+}
+
+/*
+       RMR messages must be released by RMR as there might be transport
+       buffers that have to be dealt with. Every callback is expected to
+       call this function when finished with the message.
+*/
+void Messenger::Release_mbuf( void* vmbuf ) {
+       rmr_free_msg( (rmr_mbuf_t *)  vmbuf );
+}
+
+/*
+       Wait for clear to send.
+       Until RMR loads a route table, all sends will fail with a
+       "no endpoint" state.  This function allows the user application
+       to block until RMR has a viable route table.  It does not guarentee
+       that every message that the user app will try to send has an entry.
+
+       The use of this function by the user application allows for the
+       parallel initialisation of the application while waiting for the
+       route table service to generate a table for the application. The
+       initialisation function may be callsed with "no wait" and this
+       function invoked when the application has completed initialisation
+       and is ready to start sending messages.
+
+       The max wait parameter is the maximum number of seconds to block.
+       If RMR never reports ready false is returned.  A true return
+       incidcates all is ready.  If max_wait is 0, then this will only
+       return when RMR is ready to send.
+*/
+bool Messenger::Wait_for_cts( int max_wait ) {
+       bool block_4ever;
+
+       block_4ever = max_wait == 0;
+       while( block_4ever || max_wait > 0 ) {
+               if( rmr_ready( mrc ) ) {
+                       return true;
+               }
+
+               sleep( 1 );
+               max_wait--;
+       }
+
+       return false;
+}
diff --git a/src/messaging/messenger.hpp b/src/messaging/messenger.hpp
new file mode 100644 (file)
index 0000000..0fa7899
--- /dev/null
@@ -0,0 +1,78 @@
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+*/
+
+
+/*
+       Mnemonic:       messenger.hpp
+       Abstract:       Headers for the messenger class
+
+       Date:           10 March 2020
+       Author:         E. Scott Daniels
+*/
+
+#ifndef _messenger_hpp
+#define _messenger_hpp
+
+
+#include <iostream>
+#include <string>
+#include <map>
+#include <memory>
+#include <mutex>
+
+#include <rmr/rmr.h>
+
+//#include "callback.hpp"
+//#include "default_cb.hpp"            // default callback prototypes
+#include "message.hpp"
+
+#ifndef RMR_FALSE
+       #define RMR_FALSE       0
+       #define RMR_TRUE        1
+#endif
+
+class Messenger {
+
+       private:
+               std::map<int,Callback*> cb_hash;        // callback functions associated with message types
+               std::mutex*     gate;                                   // overall mutex should we need searialisation
+               bool            ok_2_run;
+               bool            callbacks = false;              // true if callbacks are defined
+               void*           mrc;                                    // message router context
+               char*           listen_port;                    // port we ask msg router to listen on
+
+       public:
+               // -- constants which need an instance; that happens as a global in the .cpp file (wtf C++)
+               static const int MAX_PAYLOAD;                   // max message size we'll handle
+               static const int DEFAULT_CALLBACK;              // parm for add callback to set default
+
+               Messenger( char* port, bool wait4table );       // builder
+               ~Messenger();                                                           // destroyer
+
+               void Add_msg_cb( int mtype, user_callback fun_name, void* data );
+               std::unique_ptr<Message> Alloc_msg( int payload_size );                 // message allocation
+               void Listen( );                                                                                                 // lisen driver
+               std::unique_ptr<Message> Receive( int timeout );                                // receive 1 message
+               void Stop( );                                                                                                   // force to stop
+               void Release_mbuf( void* vmbuf );
+               bool Wait_for_cts( int max_wait );
+};
+
+#endif
diff --git a/src/messaging/msg_component.hpp b/src/messaging/msg_component.hpp
new file mode 100644 (file)
index 0000000..d707257
--- /dev/null
@@ -0,0 +1,57 @@
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+*/
+
+/*
+       Mnemonic:       msg_component.hpp
+       Abstract:       Defines a message component type which is needed in order
+                               to use smart pointers (unique) to point at bytes in the
+                               RMR message (which are not directly allocated and cannot
+                               be freed/deleted outside of RMR (require a special destruction
+                               call in the smart pointer).
+
+       Date:           17 March 2020
+       Author:         E. Scott Daniels
+*/
+
+#ifndef _MSG_COMPONENT_HPP
+#define _MSG_COMPONENT_HPP
+
+#include <memory>
+
+//  -------------- smart pointer support  --------------------------------
+/*
+       Pointers to a lot of things in the RMR message aren't directly
+       allocated and thus cannot be directly freed. To return a smart
+       pointer to these we have to ensure that no attempt to free/delete
+       the reference is made.  This struct defines a type with a function
+       pointer (operator) that is 'registered' as the delete function for
+       such a smart pointer, and does _nothing_ when called.
+*/
+typedef struct {
+       void operator()( unsigned char * p ){}
+} unfreeable;
+
+/*
+       A 'generic' smart pointer to a component in the message which cannot
+       be directly freed (e.g. the payload, meid, etc).
+*/
+using Msg_component = std::unique_ptr<unsigned char, unfreeable>;
+
+#endif
diff --git a/src/xapp/CMakeLists.txt b/src/xapp/CMakeLists.txt
new file mode 100644 (file)
index 0000000..093ef6f
--- /dev/null
@@ -0,0 +1,40 @@
+# vim: sw=4 ts=4 noet:
+#
+#==================================================================================
+#   Copyright (c) 2020 Nokia
+#   Copyright (c) 2020 AT&T Intellectual Property.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#==================================================================================
+#
+
+
+# For clarity: this generates object, not a lib as the CM command implies.
+#
+add_library( xapp_objects OBJECT
+       xapp.cpp        
+)
+
+target_include_directories (xapp_objects PUBLIC
+       $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+       $<INSTALL_INTERFACE:include>
+       PRIVATE src)
+
+# header files should go into .../include/xfcpp/
+if( DEV_PKG )
+       install( FILES
+               xapp.hpp  
+               DESTINATION ${install_inc}
+       ) 
+endif()
+
diff --git a/src/xapp/README b/src/xapp/README
new file mode 100644 (file)
index 0000000..39391b2
--- /dev/null
@@ -0,0 +1,27 @@
+
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+
+This directory contains the source for the overall Xapp class.
+Specifially:
+
+       xapp.*
+               Is the class that the user creates in their application. It provides, by
+               extension, the RMR messaging functions.
+
+
+       
diff --git a/src/xapp/xapp.cpp b/src/xapp/xapp.cpp
new file mode 100644 (file)
index 0000000..02cea30
--- /dev/null
@@ -0,0 +1,99 @@
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+*/
+
+/*
+       Mnemonic:       xapp.cpp
+       Abstract:       The main xapp class.
+                               This class actually extends the messenger class as most of what
+                               would be done here is just passing through to the messenger.
+
+       Date:           13 March 2020
+       Author:         E. Scott Daniels
+*/
+
+#include <string.h>
+#include <unistd.h>
+
+#include <rmr/rmr.h>
+#include <rmr/RIC_message_types.h>
+
+
+#include <iostream>
+#include <thread>
+
+#include <messenger.hpp>
+#include "xapp.hpp"
+
+// --------------- private -----------------------------------------------------
+
+
+
+// --------------- builders -----------------------------------------------
+
+/*
+       If wait4table is true, then the construction of the object does not
+       complete until the underlying transport has a new copy of the route
+       table.
+
+       If port is nil, then the default port is used (4560).
+*/
+Xapp::Xapp( char* port, bool wait4table ) : Messenger( port, wait4table ) {
+       // what's left to do?
+}
+
+/*
+       Destroyer.
+*/
+Xapp::~Xapp() {
+       // nothing to destroy; superclass destruction is magically invoked.
+}
+
+/*
+       Start n threads, each running a listener which will drive callbacks.
+
+       The final listener is started in the main thread; in otherwords this
+       function won't return unless that listener crashes.
+*/
+void Xapp::Run( int nthreads ) {
+       int joined;                                             // at end, number of threads joined back
+       int i;
+       std::thread** tinfo;                            // each thread we'll start
+
+       tinfo = new std::thread* [nthreads-1];
+
+       for( i = 0; i < nthreads - 1; i++ ) {                           // thread for each n-1; last runs here
+               tinfo[i] = new std::thread( &Xapp::Listen, this );
+       }
+
+       this->Listen();                                         // will return only when halted
+
+       for( i = 0; i < nthreads - 1; i++ ) {                           // wait for others to stop
+               tinfo[i]->join();
+       }
+}
+
+/*
+       Halt the xapp. This will drive the messenger's stop function to prevent any
+       active listeners from running, and will shut things down.
+*/
+void Xapp::Halt() {
+       this->Stop();                   // messenger shut off
+}
+
diff --git a/src/xapp/xapp.hpp b/src/xapp/xapp.hpp
new file mode 100644 (file)
index 0000000..a3d5e97
--- /dev/null
@@ -0,0 +1,59 @@
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================================
+*/
+
+/*
+       Mnemonic:       xapp.hpp
+       Abstract:       Headers for the xapp class. This class extends the messenger class
+                               as most of the function of this class would be just passing through
+                               calls to the messenger object.
+
+       Date:           13 March 2020
+       Author:         E. Scott Daniels
+*/
+
+#ifndef _xapp_hpp
+#define _xapp_hpp
+
+
+#include <iostream>
+#include <string>
+#include <mutex>
+#include <map>
+
+#include <rmr/rmr.h>
+
+#include "callback.hpp"
+#include "messenger.hpp"
+
+class Xapp : public Messenger {
+
+       private:
+               std::string name;
+
+       public:
+               Xapp( char* listen_port, bool wait4rt );        // builder
+               Xapp( );
+               ~Xapp();                                                                        // destroyer
+
+               void Run( int nthreads );                                       // message listen driver
+               void Halt( );                                                           // force to stop
+};
+
+#endif