--- /dev/null
+#==================================================================================
+# 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 "2" )
+
+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()
+
+# -------- unit testing -------------------------------------------------------
+enable_testing()
+add_test(
+ NAME drive_unit_tests
+ COMMAND bash ../test/unit_test.sh -q
+ WORKING_DIRECTORY ../test
+)
+
+
+# ------------- 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()
--- /dev/null
+# vim: ts=4 sw=4 noet:
+#==================================================================================
+# 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.
+#==================================================================================
+
+
+# --------------------------------------------------------------------------------------
+# Mnemonic: Dockerfile
+# Abstract: This can be used to create a base environment for using the xAPP
+# framework. It will install RMR and the framework libraries. It also
+# installs make and g++ so that it can be used as a builder environment.
+#
+# The unit tests are executed as a part of the build process; if they are
+# not passing then the build will fail.
+#
+# Building should be as simple as:
+#
+# docker build -f Dockerfile -t ricxfcpp:[version]
+#
+# Date: 23 March 2020
+# Author: E. Scott Daniels
+# --------------------------------------------------------------------------------------
+
+
+FROM nexus3.o-ran-sc.org:10004/bldr-ubuntu18-c-go:6-u18.04-nng as buildenv
+RUN mkdir /playpen
+
+RUN apt-get update && apt-get install -y cmake gcc make git g++ wget
+
+RUN mkdir /playpen/bin /playpen/factory /playpen/factory/src /playpen/factory/test
+ARG SRC=.
+
+WORKDIR /playpen
+# Install RMr (runtime and dev) from debian package cached on packagecloud.io
+ARG RMR_VER=3.6.2
+
+# if package cloud is actually working, this is preferred
+#
+#RUN wget -nv --content-disposition https://packagecloud.io/o-ran-sc/staging/packages/debian/stretch/rmr_${RMR_VER}_amd64.deb/download.deb
+#RUN wget -nv --content-disposition https://packagecloud.io/o-ran-sc/staging/packages/debian/stretch/rmr-dev_${RMR_VER}_amd64.deb/download.deb
+#RUN dpkg -i rmr_${RMR_VER}_amd64.deb
+#RUN dpkg -i rmr-dev_${RMR_VER}_amd64.deb
+#
+# else this:
+#
+RUN git config --global http.proxy http://one.proxy.att.com:8080
+
+
+RUN apt-get install -y cpputest
+RUN apt-get remove -y libboost-all-dev
+RUN apt-get install -y libboost-all-dev
+RUN apt-get install -y libhiredis-dev
+RUN apt-get install -y valgrind
+
+
+RUN git clone https://gerrit.o-ran-sc.org/r/ric-plt/sdl
+RUN cd sdl && \
+ ./autogen.sh && \
+ ./configure && \
+ make all && \
+ # make test && \
+ make install
+
+
+COPY ${SRC}/build_rmr.sh /playpen/bin
+RUN bash /playpen/bin/build_rmr.sh -t ${RMR_VER}
+
+COPY ${SRC}/CMakeLists.txt /playpen/factory/
+COPY ${SRC}/src /playpen/factory/src/
+COPY ${SRC}/test /playpen/factory/test/
+#COPY ${SRC}/examples /tmp/examples/
+
+
+COPY ${SRC}/examples /playpen/factory/examples
+COPY ${SRC}/routes.txt /playpen/factory/examples
+
+#
+# Run unit tests
+#
+COPY ${SRC}/test/* /playpen/factory/test/
+RUN cd /playpen/factory/test; bash unit_test.sh
+
+# Go to the factory and build our stuff
+#
+ENV LD_LIBRARY_PATH=/usr/local/lib64:/usr/local/lib:/usr/lib
+ENV C_INCLUDE_PATH=/usr/local/include
+RUN cd /playpen/factory; rm -fr .build; mkdir .build; cd .build; cmake .. -DDEV_PKG=1; make install; cmake .. -DDEV_PKG=0; make install
+
+RUN cd /playpen/factory/examples; make
+
+RUN ls /usr/local/lib
+
+
+# ----- final, smaller image ----------------------------------
+FROM ubuntu:18.04
+
+# must add compile tools to make it a builder environmnent. If a build environment isn't needed
+# comment out the next line and reduce the image size by more than 180M.
+#
+RUN apt-get update && apt-get install -y --no-install-recommends make g++
+
+# if bash doesn't cut it for run_replay grab a real shell and clean up as much as we can
+RUN apt-get update; apt-get install -y ksh
+RUN rm -fr /var/lib/apt/lists
+
+RUN mkdir -p /usr/local/include/ricxfcpp
+COPY --from=buildenv /usr/local/lib /usr/local/lib/
+COPY --from=buildenv /usr/local/include/ricxfcpp /usr/local/include/ricxfcpp/
+COPY --from=buildenv /usr/local/include/rmr /usr/local/include/rmr/
+COPY --from=buildenv /usr/local/lib /usr/local/lib
+COPY --from=buildenv /usr/lib /usr/lib/
+
+RUN mkdir -p /examples/
+COPY --from=buildenv /playpen/factory/examples/ts_xapp /examples/
+COPY --from=buildenv /playpen/factory/examples/routes.txt /examples/
+
+
+ENV LD_LIBRARY_PATH=/usr/local/lib64:/usr/local/lib
+ENV C_INCLUDE_PATH=/usr/local/include
+
+ENV RMR_SEED_RT="routes.txt"
+
+#WORKDIR /factory
+
+WORKDIR /examples/
+
+#CMD [ "make" ]
+
+CMD ./ts_xapp
--- /dev/null
+
+==================================================================================
+ 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.
+==================================================================================
+
+xAPP C++ Framework
+
+This repository contains a "framework" on which C++ RIC applications (xAPPs)
+can be built.
--- /dev/null
+#!/usr/bin/env ksh
+
+# Mnemonic: rmr_build.sh
+# Abstract: This will pull RMR from the repo, build and install. This
+# is en lieu of using wget to fetch the RMR package from some
+# repo and installing it. The package method is preferred
+# but if that breaks this can be used in place of it.
+
+rmr_ver=${1:-3.6.2}
+
+# assume that we're in the proper directory
+set -e
+git clone "https://gerrit.o-ran-sc.org/r/ric-plt/lib/rmr"
+
+cd rmr
+git checkout $ver
+mkdir .build
+cd .build
+cmake .. -DDEV_PKG=1 -DPACK_EXTERNALS=1
+make install
+cmake .. -DDEV_PKG=0
+make install
+
--- /dev/null
+# 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
+
+% :: %.cpp
+ g++ $< -g -o $@ -lricxfcpp -lrmr_si -lpthread -lm -lsdl
+
+all:: ts_xapp
--- /dev/null
+// 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: ts_xapp.cpp
+ Abstract: Traffic Steering xApp;
+ 1. Receives A1 Policy
+ 2. Queries SDL to decide which UE to attempt Traffic Steering for
+ 3. Requests prediction for UE throughput on current and neighbor cells
+ 4. Receives prediction
+ 5. Optionally exercises Traffic Steering action over E2
+
+ Date: 22 April 2020
+ Author: Ron Shacham
+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+
+#include <sdl/syncstorage.hpp>
+#include <set>
+#include <map>
+#include <vector>
+#include <string>
+
+#include "ricxfcpp/xapp.hpp"
+
+using Namespace = std::string;
+using Key = std::string;
+using Data = std::vector<uint8_t>;
+using DataMap = std::map<Key, Data>;
+using Keys = std::set<Key>;
+
+
+// ----------------------------------------------------------
+
+std::unique_ptr<Xapp> xfw;
+
+
+void policy_callback( Message& mbuf, int mtype, int subid, int len, Msg_component payload, void* data ) {
+
+ long now;
+ long total_count;
+
+ int sz;
+ int i;
+
+ int response_to = 0; // max timeout wating for a response
+
+ int send_mtype = 0;
+ int rmtype; // received message type
+ int delay = 1000000; // mu-sec delay; default 1s
+
+ std::unique_ptr<Message> msg;
+ Msg_component send_payload; // special type of unique pointer to the payload
+
+ fprintf( stderr, "Policy Callback got a message, type=%d , length=%d\n" , mtype, len);
+ fprintf(stderr, "payload is %s\n", payload.get());
+
+ //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" );
+
+ mtype = 0;
+
+ fprintf(stderr, "cb 1\n");
+
+ msg = xfw->Alloc_msg( 2048 );
+
+ 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 );
+ }
+
+ fprintf(stderr, "cb 2");
+
+ send_payload = msg->Get_payload(); // direct access to payload
+ snprintf( (char *) send_payload.get(), 2048, "{\"UEPredictionSet\" : [\"222\", \"333\", \"444\"]}", i );
+
+ fprintf(stderr, "cb 3");
+
+ // payload updated in place, nothing to copy from, so payload parm is nil
+ if ( ! msg->Send_msg( mtype, Message::NO_SUBID, strlen( (char *) send_payload.get() )+1, NULL )) {
+ fprintf( stderr, "<SNDR> send failed: %d\n", i );
+ }
+
+ fprintf(stderr, "cb 4");
+
+ /*
+ msg = xfw->Receive( response_to );
+ if( msg != NULL ) {
+ rmtype = msg->Get_mtype();
+ send_payload = msg->Get_payload();
+ fprintf( stderr, "got: mtype=%d payload=(%s)\n", rmtype, (char *) send_payload.get() );
+ }
+ */
+
+}
+
+
+void prediction_callback( Message& mbuf, int mtype, int subid, int len, Msg_component payload, void* data ) {
+
+ long now;
+ long total_count;
+
+ int sz;
+ int i;
+
+ int response_to = 0; // max timeout wating for a response
+
+ int send_mtype = 0;
+ int rmtype; // received message type
+ int delay = 1000000; // mu-sec delay; default 1s
+
+ std::unique_ptr<Message> msg;
+ Msg_component send_payload; // special type of unique pointer to the payload
+
+ fprintf( stderr, "Prediction Callback got a message, type=%d , length=%d\n" , mtype, len);
+ fprintf(stderr, "payload is %s\n", payload.get());
+
+ 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" );
+
+ mtype = 0;
+
+ fprintf(stderr, "cb 1\n");
+
+}
+
+
+
+extern int main( int argc, char** argv ) {
+
+ std::unique_ptr<Message> msg;
+ Msg_component payload; // special type of unique pointer to the payload
+
+ int nthreads = 1;
+
+ int response_to = 0; // max timeout wating for a response
+
+ int delay = 1000000; // mu-sec delay; default 1s
+
+ char* port = (char *) "4560";
+
+ int ai;
+
+ 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 'd': // delay between messages (mu-sec)
+ delay = atoi( argv[ai+1] );
+ ai++;
+ break;
+
+ 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
+
+ fprintf(stderr, "code1\n");
+
+ xfw->Add_msg_cb( 20010, policy_callback, NULL );
+ xfw->Add_msg_cb( 30002, prediction_callback, NULL );
+
+ fprintf(stderr, "code2\n");
+
+ std::string sdl_namespace_u = "TS-UE-metrics";
+ std::string sdl_namespace_c = "TS-cell-metrics";
+
+ fprintf(stderr, "code5\n");
+
+ std::unique_ptr<shareddatalayer::SyncStorage> sdl(shareddatalayer::SyncStorage::create());
+
+ Namespace nsu(sdl_namespace_u);
+ Namespace nsc(sdl_namespace_c);
+
+ /*
+
+ fprintf(stderr, "before sdl set\n");
+
+ try{
+ //connecting to the Redis and generating a random key for namespace "hwxapp"
+ fprintf(stderr, "IN SDL Set Data");
+ // std::string data_string = "{\"rsrp\" : -110}";
+
+
+ std::string data_string = "{\"CellID\": \"310-680-200-555001\", \"MeasTimestampPDCPBytes\": \"2020-03-18 02:23:18.220\", \"MeasPeriodPDCPBytes\": 20, \"PDCPBytesDL\": 2000000, \"PDCPBytesUL\": 1200000, \"MeasTimestampAvailPRB\": \"2020-03-18 02:23:18.220\", \"MeasPeriodAvailPRB\": 20, \"AvailPRBDL\": 30, \"AvailPRBUL\": 50 }";
+
+ DataMap dmap;
+ // char key[4]="abc";
+ char key[] = "310-680-200-555001";
+ std::cout << "KEY: "<< key << std::endl;
+ Key k = key;
+ Data d;
+ // uint8_t num = 101;
+ d.assign(data_string.begin(), data_string.end());
+ // d.push_back(num);
+ dmap.insert({k,d});
+
+ sdl->set(nsc, dmap);
+
+ data_string = "{ \"CellID\": \"310-680-200-555002\", \"MeasTimestampPDCPBytes\": \"2020-03-18 02:23:18.220\", \"MeasPeriodPDCPBytes\": 20, \"PDCPBytesDL\": 800000, \"PDCPBytesUL\": 400000, \"MeasTimestampAvailPRB\": \"2020-03-18 02:23:18.220\", \"MeasPeriodAvailPRB\": 20, \"AvailPRBDL\": 30, \"AvailPRBUL\": 45 }";
+
+ Data d2;
+ DataMap dmap2;
+ char key2[] = "310-680-200-555002";
+ std::cout << "KEY: "<< key2 << std::endl;
+ Key k2 = key2;
+ d2.assign(data_string.begin(), data_string.end());
+ // d.push_back(num);
+ dmap2.insert({k2,d});
+
+ sdl->set(nsc, dmap2);
+
+
+
+ std::string data_string = "{ \"CellID\": \"310-680-200-555003\", \"MeasTimestampPDCPBytes\": \"2020-03-18 02:23:18.220\", \"MeasPeriodPDCPBytes\": 20, \"PDCPBytesDL\": 800000, \"PDCPBytesUL\": 400000, \"MeasTimestampAvailPRB\": \"2020-03-18 02:23:18.220\", \"MeasPeriodAvailPRB\": 20, \"AvailPRBDL\": 30, \"AvailPRBUL\": 45 }";
+
+ Data d3;
+ DataMap dmap3;
+ char key3[] = "310-680-200-555003";
+ std::cout << "KEY: "<< key3 << std::endl;
+ Key k3 = key3;
+ d3.assign(data_string.begin(), data_string.end());
+ // d.push_back(num);
+ dmap3.insert({k3,d3});
+
+ sdl->set(nsc, dmap3);
+
+
+
+ data_string = "{ \"UEID\": 12345, \"ServingCellID\": \"310-680-200-555002\", \"MeasTimestampUEPDCPBytes\": \"2020-03-18 02:23:18.220\", \"MeasPeriodUEPDCPBytes\": 20,\"UEPDCPBytesDL\": 250000,\"UEPDCPBytesUL\": 100000, \"MeasTimestampUEPRBUsage\": \"2020-03-18 02:23:18.220\", \"MeasPeriodUEPRBUsage\": 20, \"UEPRBUsageDL\": 10, \"UEPRBUsageUL\": 30, \"MeasTimestampRF\": \"2020-03-18 02:23:18.210\",\"MeasPeriodRF\": 40, \"ServingCellRF\": [-115,-16,-5], \"NeighborCellRF\": [ {\"CID\": \"310-680-200-555001\",\"Cell-RF\": [-90,-13,-2.5 ] }, {\"CID\": \"310-680-200-555003\", \"Cell-RF\": [-140,-17,-6 ] } ] }";
+
+ Data d4;
+ DataMap dmap4;
+ char key4[] = "12345";
+ std::cout << "KEY: "<< key << std::endl;
+ d4.assign(data_string.begin(), data_string.end());
+ Key k4 = key4;
+ // d.push_back(num);
+ dmap4.insert({k4,d4});
+
+ sdl->set(nsu, dmap4);
+
+
+ }
+ catch(...){
+ fprintf(stderr,"SDL Error in Set Data for Namespace");
+ return false;
+ }
+
+ fprintf(stderr, "after sdl set\n");
+
+ */
+
+ fprintf(stderr, "before sdl get\n");
+
+
+ std::string prefix2="310";
+ Keys K = sdl->findKeys(nsc, prefix2); // just the prefix
+ DataMap Dk = sdl->get(nsc, K);
+
+ std::cout << "K contains " << K.size() << " elements.\n";
+
+ fprintf(stderr, "before forloop\n");
+
+ for(auto si=K.begin();si!=K.end();++si){
+ std::vector<uint8_t> val_v = Dk[(*si)]; // 4 lines to unpack a string
+ char val[val_v.size()+1]; // from Data
+ int i;
+ for(i=0;i<val_v.size();++i) val[i] = (char)(val_v[i]);
+ val[i]='\0';
+ fprintf(stderr, "KEYS and Values %s = %s\n",(*si).c_str(), val);
+ }
+
+
+ std::string prefix3="12";
+ Keys K2 = sdl->findKeys(nsu, prefix3); // just the prefix
+ DataMap Dk2 = sdl->get(nsu, K2);
+
+ std::cout << "K contains " << K2.size() << " elements.\n";
+
+ fprintf(stderr, "before forloop\n");
+
+ for(auto si=K2.begin();si!=K2.end();++si){
+ std::vector<uint8_t> val_v = Dk2[(*si)]; // 4 lines to unpack a string
+ char val[val_v.size()+1]; // from Data
+ int i;
+ for(i=0;i<val_v.size();++i) val[i] = (char)(val_v[i]);
+ val[i]='\0';
+ fprintf(stderr, "KEYS and Values %s = %s\n",(*si).c_str(), val);
+ }
+
+
+ fprintf(stderr, "after sdl get\n");
+
+ xfw->Run( nthreads );
+
+ fprintf(stderr, "code3\n");
+
+ msg = xfw->Alloc_msg( 2048 );
+
+ fprintf(stderr, "code4\n");
+
+
+}
--- /dev/null
+// 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: ts_xapp.cpp
+ Abstract: Traffic Steering xApp;
+ 1. Receives A1 Policy
+ 2. Queries SDL to decide which UE to attempt Traffic Steering for
+ 3. Requests prediction for UE throughput on current and neighbor cells
+ 4. Receives prediction
+ 5. Optionally exercises Traffic Steering action over E2
+
+ Date: 22 April 2020
+ Author: Ron Shacham
+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+
+#include <sdl/syncstorage.hpp>
+#include <set>
+#include <map>
+#include <vector>
+#include <string>
+
+#include "ricxfcpp/xapp.hpp"
+
+using Namespace = std::string;
+using Key = std::string;
+using Data = std::vector<uint8_t>;
+using DataMap = std::map<Key, Data>;
+using Keys = std::set<Key>;
+
+
+// ----------------------------------------------------------
+
+std::unique_ptr<Xapp> xfw;
+
+
+void policy_callback( Message& mbuf, int mtype, int subid, int len, Msg_component payload, void* data ) {
+
+ long now;
+ long total_count;
+
+ int sz;
+ int i;
+
+ int response_to = 0; // max timeout wating for a response
+
+ int send_mtype = 0;
+ int rmtype; // received message type
+ int delay = 1000000; // mu-sec delay; default 1s
+
+ std::unique_ptr<Message> msg;
+ Msg_component send_payload; // special type of unique pointer to the payload
+
+ fprintf( stderr, "Policy Callback got a message, type=%d , length=%d\n" , mtype, len);
+ fprintf(stderr, "payload is %s\n", payload.get());
+
+ //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" );
+
+ mtype = 0;
+
+ fprintf(stderr, "cb 1\n");
+
+ msg = xfw->Alloc_msg( 2048 );
+
+ 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 );
+ }
+
+ fprintf(stderr, "cb 2");
+
+ send_payload = msg->Get_payload(); // direct access to payload
+ snprintf( (char *) send_payload.get(), 2048, "{\"UEPredictionSet\" : [\"222\", \"333\", \"444\"]}", i );
+
+ fprintf(stderr, "cb 3");
+
+ // payload updated in place, nothing to copy from, so payload parm is nil
+ if ( ! msg->Send_msg( mtype, Message::NO_SUBID, strlen( (char *) send_payload.get() )+1, NULL )) {
+ fprintf( stderr, "<SNDR> send failed: %d\n", i );
+ }
+
+ fprintf(stderr, "cb 4");
+
+ /*
+ msg = xfw->Receive( response_to );
+ if( msg != NULL ) {
+ rmtype = msg->Get_mtype();
+ send_payload = msg->Get_payload();
+ fprintf( stderr, "got: mtype=%d payload=(%s)\n", rmtype, (char *) send_payload.get() );
+ }
+ */
+
+}
+
+
+void prediction_callback( Message& mbuf, int mtype, int subid, int len, Msg_component payload, void* data ) {
+
+ long now;
+ long total_count;
+
+ int sz;
+ int i;
+
+ int response_to = 0; // max timeout wating for a response
+
+ int send_mtype = 0;
+ int rmtype; // received message type
+ int delay = 1000000; // mu-sec delay; default 1s
+
+ std::unique_ptr<Message> msg;
+ Msg_component send_payload; // special type of unique pointer to the payload
+
+ fprintf( stderr, "Prediction Callback got a message, type=%d , length=%d\n" , mtype, len);
+ fprintf(stderr, "payload is %s\n", payload.get());
+
+ 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" );
+
+ mtype = 0;
+
+ fprintf(stderr, "cb 1\n");
+
+}
+
+
+
+extern int main( int argc, char** argv ) {
+
+ std::unique_ptr<Message> msg;
+ Msg_component payload; // special type of unique pointer to the payload
+
+ int nthreads = 1;
+
+ int response_to = 0; // max timeout wating for a response
+
+ int delay = 1000000; // mu-sec delay; default 1s
+
+ char* port = (char *) "4555";
+
+ int ai;
+
+ 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 'd': // delay between messages (mu-sec)
+ delay = atoi( argv[ai+1] );
+ ai++;
+ break;
+
+ 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
+
+ fprintf(stderr, "code1\n");
+
+ xfw->Add_msg_cb( 20010, policy_callback, NULL );
+ xfw->Add_msg_cb( 30002, prediction_callback, NULL );
+
+ fprintf(stderr, "code2\n");
+
+ std::string sdl_namespace_u = "TS-UE-metrics";
+ std::string sdl_namespace_c = "TS-cell-metrics";
+
+ fprintf(stderr, "code5\n");
+
+ std::unique_ptr<shareddatalayer::SyncStorage> sdl(shareddatalayer::SyncStorage::create());
+
+ Namespace nsu(sdl_namespace_u);
+ Namespace nsc(sdl_namespace_c);
+
+ /*
+
+ fprintf(stderr, "before sdl set\n");
+
+ try{
+ //connecting to the Redis and generating a random key for namespace "hwxapp"
+ fprintf(stderr, "IN SDL Set Data");
+ // std::string data_string = "{\"rsrp\" : -110}";
+
+
+ std::string data_string = "{\"CellID\": \"310-680-200-555001\", \"MeasTimestampPDCPBytes\": \"2020-03-18 02:23:18.220\", \"MeasPeriodPDCPBytes\": 20, \"PDCPBytesDL\": 2000000, \"PDCPBytesUL\": 1200000, \"MeasTimestampAvailPRB\": \"2020-03-18 02:23:18.220\", \"MeasPeriodAvailPRB\": 20, \"AvailPRBDL\": 30, \"AvailPRBUL\": 50 }";
+
+ DataMap dmap;
+ // char key[4]="abc";
+ char key[] = "310-680-200-555001";
+ std::cout << "KEY: "<< key << std::endl;
+ Key k = key;
+ Data d;
+ // uint8_t num = 101;
+ d.assign(data_string.begin(), data_string.end());
+ // d.push_back(num);
+ dmap.insert({k,d});
+
+ sdl->set(nsc, dmap);
+
+ data_string = "{ \"CellID\": \"310-680-200-555002\", \"MeasTimestampPDCPBytes\": \"2020-03-18 02:23:18.220\", \"MeasPeriodPDCPBytes\": 20, \"PDCPBytesDL\": 800000, \"PDCPBytesUL\": 400000, \"MeasTimestampAvailPRB\": \"2020-03-18 02:23:18.220\", \"MeasPeriodAvailPRB\": 20, \"AvailPRBDL\": 30, \"AvailPRBUL\": 45 }";
+
+ Data d2;
+ DataMap dmap2;
+ char key2[] = "310-680-200-555002";
+ std::cout << "KEY: "<< key2 << std::endl;
+ Key k2 = key2;
+ d2.assign(data_string.begin(), data_string.end());
+ // d.push_back(num);
+ dmap2.insert({k2,d});
+
+ sdl->set(nsc, dmap2);
+
+
+
+ std::string data_string = "{ \"CellID\": \"310-680-200-555003\", \"MeasTimestampPDCPBytes\": \"2020-03-18 02:23:18.220\", \"MeasPeriodPDCPBytes\": 20, \"PDCPBytesDL\": 800000, \"PDCPBytesUL\": 400000, \"MeasTimestampAvailPRB\": \"2020-03-18 02:23:18.220\", \"MeasPeriodAvailPRB\": 20, \"AvailPRBDL\": 30, \"AvailPRBUL\": 45 }";
+
+ Data d3;
+ DataMap dmap3;
+ char key3[] = "310-680-200-555003";
+ std::cout << "KEY: "<< key3 << std::endl;
+ Key k3 = key3;
+ d3.assign(data_string.begin(), data_string.end());
+ // d.push_back(num);
+ dmap3.insert({k3,d3});
+
+ sdl->set(nsc, dmap3);
+
+
+
+ data_string = "{ \"UEID\": 12345, \"ServingCellID\": \"310-680-200-555002\", \"MeasTimestampUEPDCPBytes\": \"2020-03-18 02:23:18.220\", \"MeasPeriodUEPDCPBytes\": 20,\"UEPDCPBytesDL\": 250000,\"UEPDCPBytesUL\": 100000, \"MeasTimestampUEPRBUsage\": \"2020-03-18 02:23:18.220\", \"MeasPeriodUEPRBUsage\": 20, \"UEPRBUsageDL\": 10, \"UEPRBUsageUL\": 30, \"MeasTimestampRF\": \"2020-03-18 02:23:18.210\",\"MeasPeriodRF\": 40, \"ServingCellRF\": [-115,-16,-5], \"NeighborCellRF\": [ {\"CID\": \"310-680-200-555001\",\"Cell-RF\": [-90,-13,-2.5 ] }, {\"CID\": \"310-680-200-555003\", \"Cell-RF\": [-140,-17,-6 ] } ] }";
+
+ Data d4;
+ DataMap dmap4;
+ char key4[] = "12345";
+ std::cout << "KEY: "<< key << std::endl;
+ d4.assign(data_string.begin(), data_string.end());
+ Key k4 = key4;
+ // d.push_back(num);
+ dmap4.insert({k4,d4});
+
+ sdl->set(nsu, dmap4);
+
+
+ }
+ catch(...){
+ fprintf(stderr,"SDL Error in Set Data for Namespace");
+ return false;
+ }
+
+ fprintf(stderr, "after sdl set\n");
+
+ */
+
+ fprintf(stderr, "before sdl get\n");
+
+
+ std::string prefix2="310";
+ Keys K = sdl->findKeys(nsc, prefix2); // just the prefix
+ DataMap Dk = sdl->get(nsc, K);
+
+ std::cout << "K contains " << K.size() << " elements.\n";
+
+ fprintf(stderr, "before forloop\n");
+
+ for(auto si=K.begin();si!=K.end();++si){
+ std::vector<uint8_t> val_v = Dk[(*si)]; // 4 lines to unpack a string
+ char val[val_v.size()+1]; // from Data
+ int i;
+ for(i=0;i<val_v.size();++i) val[i] = (char)(val_v[i]);
+ val[i]='\0';
+ fprintf(stderr, "KEYS and Values %s = %s\n",(*si).c_str(), val);
+ }
+
+
+ std::string prefix3="12";
+ Keys K2 = sdl->findKeys(nsu, prefix3); // just the prefix
+ DataMap Dk2 = sdl->get(nsu, K2);
+
+ std::cout << "K contains " << K2.size() << " elements.\n";
+
+ fprintf(stderr, "before forloop\n");
+
+ for(auto si=K2.begin();si!=K2.end();++si){
+ std::vector<uint8_t> val_v = Dk2[(*si)]; // 4 lines to unpack a string
+ char val[val_v.size()+1]; // from Data
+ int i;
+ for(i=0;i<val_v.size();++i) val[i] = (char)(val_v[i]);
+ val[i]='\0';
+ fprintf(stderr, "KEYS and Values %s = %s\n",(*si).c_str(), val);
+ }
+
+
+ fprintf(stderr, "after sdl get\n");
+
+ xfw->Run( nthreads );
+
+ fprintf(stderr, "code3\n");
+
+ msg = xfw->Alloc_msg( 2048 );
+
+ fprintf(stderr, "code4\n");
+
+
+}
--- /dev/null
+# Communicate to CI which version of RMR to install in the build/vet environment
+---
+version: 3.6.2
--- /dev/null
+newrt|start
+mse|20010|20008|4560
+rte|20011|service-ricplt-a1mediator-rmr:10000
+rte|TS_QUE_PREDICTION|4560
+rte|TS_UE_LIST|service-ricxapp-qpd:4560
+newrt|end
--- /dev/null
+#! /bin/bash
+
+export RMR_SEED_RT="routes.txt"
+export RMR_RTG_SVC="9999"
+export XAPP_NAME="HELLOWORLD_XAPP"
+export HW_PORTS="4560"
+export MSG_MAX_BUFFER="2048"
+export THREADS="1"
+export VERBOSE="0"
+export CONFIG_FILE="config/config-file.json"
+export GNODEB="NYC123"
+export XAPP_ID="3489-er492k-92389"
+export A1_SCHEMA_FILE="schemas/hwxapp-policy.json"
+export VES_SCHEMA_FILE="schemas/hwxapp-ves.json"
+export VES_COLLECTOR_URL="127.0.0.1:6350"
+export VES_MEASUREMENT_INTERVAL="10"
+export LOG_LEVEL="MDCLOG_ERR"
+export OPERATING_MODE="CONTROL"
+
+
+
--- /dev/null
+# 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()
+
--- /dev/null
+
+==================================================================================
+ 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).
+
+
+
--- /dev/null
+// 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;
+}
+
+/*
+ there is nothing to be done from a destruction perspective, so no
+ destruction function needed at the moment.
+*/
+
+/*
+ Drive_cb will invoke the callback and pass along the stuff passed here.
+*/
+void Callback::Drive_cb( Message& m ) {
+ if( user_fun != NULL ) {
+ user_fun( m, m.Get_mtype(), m.Get_subid(), m.Get_len(), m.Get_payload(), udata );
+ }
+}
+
+
--- /dev/null
+// 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)( Message& m, int mtype, int subid, int payload_len, Msg_component payload, void* usr_data );
+
+class Callback {
+
+ private:
+ user_callback user_fun;
+ void* udata; // user data
+
+ public:
+ Callback( user_callback, void* data ); // builder
+ void Drive_cb( Message& m ); // invoker
+};
+
+
+#endif
--- /dev/null
+// 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( 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 );
+}
--- /dev/null
+// 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( Message& mbuf, int mtype, int sid, int len, Msg_component payload, void* data );
+
+
+#endif
--- /dev/null
+// 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/operators -------------------------------------
+
+/*
+ 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 );
+}
+
+/*
+ Copy builder. Given a source object instance (soi), create a copy.
+ Creating a copy should be avoided as it can be SLOW!
+*/
+Message::Message( const Message& soi ) {
+ int payload_size;
+
+ mrc = soi.mrc;
+ payload_size = rmr_payload_size( soi.mbuf ); // rmr can handle a nil pointer
+ mbuf = rmr_realloc_payload( soi.mbuf, payload_size, RMR_COPY, RMR_CLONE );
+}
+
+/*
+ Assignment operator. Simiolar to the copycat, but "this" object exists and
+ may have data that needs to be released prior to making a copy of the soi.
+*/
+Message& Message::operator=( const Message& soi ) {
+ int payload_size;
+
+ if( this != &soi ) { // cannot do self assignment
+ if( mbuf != NULL ) {
+ rmr_free_msg( mbuf ); // release the old one so we don't leak
+ }
+
+ payload_size = rmr_payload_size( soi.mbuf ); // rmr can handle a nil pointer
+ mrc = soi.mrc;
+ mbuf = rmr_realloc_payload( soi.mbuf, payload_size, RMR_COPY, RMR_CLONE );
+ }
+
+ return *this;
+}
+
+/*
+ Move builder. Given a source object instance (soi), move the information from
+ the soi ensuring that the destriction of the soi doesn't trash things from
+ under us.
+*/
+Message::Message( Message&& soi ) {
+ mrc = soi.mrc;
+ mbuf = soi.mbuf;
+
+ soi.mrc = NULL; // prevent closing of RMR stuff on soi destroy
+ soi.mbuf = NULL;
+}
+
+/*
+ Move Assignment operator. Move the message data to the existing object
+ ensure the object reference is cleaned up, and ensuring that the source
+ object references are removed.
+*/
+Message& Message::operator=( Message&& soi ) {
+ if( this != &soi ) { // cannot do self assignment
+ if( mbuf != NULL ) {
+ rmr_free_msg( mbuf ); // release the old one so we don't leak
+ }
+
+ mrc = soi.mrc;
+ mbuf = soi.mbuf;
+
+ soi.mrc = NULL;
+ soi.mbuf = NULL;
+ }
+
+ return *this;
+}
+
+
+/*
+ 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;
+
+ m = (unsigned char *) malloc( sizeof( unsigned char ) * RMR_MAX_MEID );
+ rmr_get_meid( mbuf, m );
+
+ return std::unique_ptr<unsigned char>( m );
+}
+
+/*
+ Return the total size of the payload (the amount that can be written to
+ as opposed to the portion of the payload which is currently in use.
+ If mbuf isn't valid (nil, or message has a broken header) the return
+ will be -1.
+*/
+int Message::Get_available_size(){
+ return rmr_payload_size( mbuf ); // rmr can handle a nil pointer
+}
+
+int Message::Get_mtype(){
+ int rval = INVALID_MTYPE;
+
+ if( mbuf != NULL ) {
+ rval = mbuf->mtype;
+ }
+
+ return rval;
+}
+
+/*
+ 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 );
+ memset( m, 0, 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( ){
+ int state = INVALID_STATUS;
+
+ if( mbuf != NULL ) {
+ state = mbuf->state;
+ }
+
+ return state;
+}
+
+int Message::Get_subid(){
+ int rval = INVALID_SUBID;
+
+ if( mbuf != NULL ) {
+ rval =mbuf->sub_id;
+ }
+
+ return rval;
+}
+
+/*
+ Return the amount of the payload (bytes) which is used. See
+ Get_available_size() to get the total usable space in the payload.
+*/
+int Message::Get_len(){
+ int rval = 0;
+
+ if( mbuf != NULL ) {
+ rval = mbuf->len;
+ }
+
+ return rval;
+}
+
+/*
+ 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::shared_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_len( int new_len ){
+ if( mbuf != NULL && new_len >= 0 ) {
+ mbuf->len = new_len;
+ }
+}
+
+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( ) {
+ bool state = false;
+
+ if( mbuf != NULL ) {
+ mbuf = rmr_send_msg( mrc, mbuf ); // send and pick up new mbuf
+ state = mbuf->state == RMR_OK; // overall state for caller
+ }
+
+ return state;
+}
+
+/*
+ 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( ) {
+ bool state = false;
+
+ if( mbuf != NULL ) {
+ mbuf = rmr_rts_msg( mrc, mbuf ); // send and pick up new mbuf
+ state = mbuf->state == RMR_OK; // state for caller based on send
+ }
+
+ return state;
+}
+
+/*
+ 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 ) {
+ bool state = false;
+
+ if( mbuf != NULL ) {
+ if( mtype != NO_CHANGE ) {
+ mbuf->mtype = mtype;
+ }
+ if( subid != NO_CHANGE ) {
+ mbuf->sub_id = subid;
+ }
+
+ if( payload_len != NO_CHANGE ) {
+ 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 );
+ }
+
+ state = mbuf->state == RMR_OK;
+ }
+
+ return state;
+}
+
+/*
+ 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::shared_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::shared_ptr<unsigned char> response ) {
+ return Send( NO_CHANGE, NO_CHANGE, response_len, response.get(), RESPONSE );
+}
+
+bool Message::Send_response( int response_len, unsigned char* response ) {
+ return Send( NO_CHANGE, NO_CHANGE, 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::shared_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::shared_ptr<unsigned char> payload ) {
+ return Send( NO_CHANGE, NO_CHANGE, payload_len, payload.get(), MESSAGE );
+}
+
+bool Message::Send_msg( int payload_len, unsigned char* payload ) {
+ return Send( NO_CHANGE, NO_CHANGE, payload_len, payload, MESSAGE );
+}
--- /dev/null
+// 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:
+ 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 NO_CHANGE = -99; // indicates no change to a send/reply parameter
+ 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( const Message& soi ); // copy cat
+ Message& operator=( const Message& soi ); // copy operator
+ Message( Message&& soi ); // mover
+ Message& operator=( Message&& soi ); // move operator
+ ~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::shared_ptr<unsigned char> new_meid );
+ void Set_mtype( int new_type );
+ void Set_subid( int new_subid );
+ void Set_len( int new_len );
+
+ 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::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 );
+
+ bool Send_response( int mtype, int subid, int payload_len, std::shared_ptr<unsigned char> response );
+ bool Send_response( int mtype, int subid, int payload_len, unsigned char* response );
+ bool Send_response( int payload_len, std::shared_ptr<unsigned char> response );
+ bool Send_response( int payload_len, unsigned char* response );
+};
+
+
+#endif
--- /dev/null
+// 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;
+}
+
+/*
+ Move support. We DO allow the instance to be moved as only one copy
+ remains following the move.
+ Given a source object instance (soi) we move the information to
+ the new object, and then DELETE what was moved so that when the
+ user frees the soi, it doesn't destroy what we snarfed.
+*/
+Messenger::Messenger( Messenger&& soi ) {
+ mrc = soi.mrc;
+ listen_port = soi.listen_port;
+ ok_2_run = soi.ok_2_run;
+ gate = soi.gate;
+ cb_hash = soi.cb_hash; // this seems dodgy
+
+ soi.gate = NULL;
+ soi.listen_port = NULL;
+ soi.mrc = NULL;
+}
+
+/*
+ Move operator. Given a source object instance, movee it's contents
+ to this insance. We must first clean up this instance.
+*/
+Messenger& Messenger::operator=( Messenger&& soi ) {
+ if( this != &soi ) { // cannot move onto ourself
+ if( mrc != NULL ) {
+ rmr_close( mrc );
+ }
+ if( listen_port != NULL ) {
+ free( listen_port );
+ }
+
+ mrc = soi.mrc;
+ listen_port = soi.listen_port;
+ ok_2_run = soi.ok_2_run;
+ gate = soi.gate;
+ cb_hash = soi.cb_hash; // this seems dodgy
+
+ soi.gate = NULL;
+ soi.listen_port = NULL;
+ soi.mrc = NULL;
+ }
+
+ return *this;
+}
+
+/*
+ Destroyer.
+*/
+Messenger::~Messenger() {
+ if( mrc != NULL ) {
+ rmr_close( mrc );
+ }
+
+ if( listen_port != NULL ) {
+ 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( *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 = NULL;
+
+ if( mrc != NULL ) {
+ mbuf = rmr_torcv_msg( mrc, mbuf, timeout ); // future: do we want to reuse the mbuf here?
+ if( mbuf != NULL ) {
+ m = std::unique_ptr<Message>( new Message( mbuf, mrc ) );
+ }
+ }
+
+ return m;
+}
+
+/*
+ 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;
+ bool state = false;
+
+ block_4ever = max_wait == 0;
+ while( block_4ever || max_wait > 0 ) {
+ if( rmr_ready( mrc ) ) {
+ state = true;
+ break;
+ }
+
+ sleep( 1 );
+ max_wait--;
+ }
+
+ return state;
+}
--- /dev/null
+// 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 "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
+
+ // copy and assignment are PRIVATE so that they fail if xapp tries; messenger cannot be copied!
+ Messenger( const Messenger& soi );
+ Messenger& operator=( const Messenger& soi );
+
+ 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( Messenger&& soi ); // move construction
+ Messenger& operator=( Messenger&& soi ); // move operator
+ ~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
--- /dev/null
+// 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
--- /dev/null
+# 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()
+
--- /dev/null
+
+==================================================================================
+ 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.
+
+
+
--- /dev/null
+// 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 ) {
+ // nothing to do; all handled in Messenger constructor
+}
+
+/*
+ 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 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();
+ }
+
+ delete tinfo;
+}
+
+/*
+ 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
+}
+
--- /dev/null
+// 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;
+
+ // copy and assignment are PRIVATE because we cant "clone" the listen environment
+ Xapp( const Xapp& soi );
+ Xapp& operator=( const Xapp& soi );
+
+ public:
+ Xapp( char* listen_port, bool wait4rt ); // builder
+ Xapp( );
+ ~Xapp(); // destroyer
+
+ void Run( int nthreads ); // message listen driver
+ void Halt( ); // force to stop
+};
+
+#endif
--- /dev/null
+core
+*.gcov
+*.gcda
+*.gcno
--- /dev/null
+
+coverage_opts = -ftest-coverage -fprofile-arcs
+
+binaries = unit_test
+
+# RMR emulation
+rmr_em.o:: rmr_em.c
+ cc -g rmr_em.c -c
+
+unit_test:: unit_test.cpp rmr_em.o
+ # do NOT link the xapp lib; we include all modules in the test programme
+ g++ -g $(coverage_opts) -I ../src/messaging unit_test.cpp -o unit_test rmr_em.o -lpthread
+
+# prune gcov files generated by system include files
+clean::
+ rm -f *.h.gcov *.c.gcov
+
+# ditch anything that can be rebuilt
+nuke::
+ rm -f *.a *.o *.gcov *.gcda *.gcno core a.out $(binaries)
+
--- /dev/null
+#!/usr/bin/env bash
+# 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.
+#==================================================================================
+
+#
+# Parse the .gcov file and discount any unexecuted lines which are in if()
+# blocks which are testing the result of alloc/malloc calls, or testing for
+# nil pointers. The feeling is that these might not be possible to drive
+# and shoudn't contribute to coverage deficiencies.
+#
+# In verbose mode, the .gcov file is written to stdout and any unexecuted
+# line which is discounted is marked with ===== replacing the ##### marking
+# that gcov wrote.
+#
+# The return value is 0 for pass; non-zero for fail.
+#
+function discount_ck {
+ typeset f="$1"
+
+ mct=80 # force minimum coverage threshold for passing
+
+ if [[ ! -f $f ]]
+ then
+ if [[ -f ${f##*/} ]]
+ then
+ f=${f##*/}
+ else
+ echo "cant find: $f"
+ return
+ fi
+ fi
+
+ awk -v module_cov_target=$mct \
+ -v cfail=${cfail:-WARN} \
+ -v show_all=$show_all \
+ -v full_name="${1}" \
+ -v module="${f%.*}" \
+ -v chatty=$chatty \
+ -v replace_flags=$replace_flags \
+ '
+ function spit_line( ) {
+ if( chatty ) {
+ printf( "%s\n", $0 )
+ }
+ }
+
+ /-:/ { # skip unexecutable lines
+ spit_line()
+ seq++ # allow blank lines in a sequence group
+ next
+ }
+
+ {
+ nexec++ # number of executable lines
+ }
+
+ /#####:/ {
+ unexec++;
+ if( $2+0 != seq+1 ) {
+ prev_malloc = 0
+ prev_if = 0
+ seq = 0
+ spit_line()
+ next
+ }
+
+ if( prev_if && prev_malloc ) {
+ if( prev_malloc ) {
+ #printf( "allow discount: %s\n", $0 )
+ if( replace_flags ) {
+ gsub( "#####", " 1", $0 )
+ //gsub( "#####", "=====", $0 )
+ }
+ discount++;
+ }
+ }
+
+ seq++;;
+ spit_line()
+ next;
+ }
+
+ /if[(].*alloc.*{/ { # if( (x = malloc( ... )) != NULL ) or if( (p = sym_alloc(...)) != NULL )
+ seq = $2+0
+ prev_malloc = 1
+ prev_if = 1
+ spit_line()
+ next
+ }
+
+ /if[(].* == NULL/ { # a nil check likely not easily forced if it wasnt driven
+ prev_malloc = 1
+ prev_if = 1
+ spit_line()
+ seq = $2+0
+ next
+ }
+
+ /if[(]/ {
+ if( seq+1 == $2+0 && prev_malloc ) { // malloc on previous line
+ prev_if = 1
+ } else {
+ prev_malloc = 0
+ prev_if = 0
+ }
+ spit_line()
+ next
+ }
+
+ /alloc[(]/ {
+ seq = $2+0
+ prev_malloc = 1
+ spit_line()
+ next
+ }
+
+ {
+ spit_line()
+ }
+
+ END {
+ net = unexec - discount
+ orig_cov = ((nexec-unexec)/nexec)*100 # original coverage
+ adj_cov = ((nexec-net)/nexec)*100 # coverage after discount
+ pass_fail = adj_cov < module_cov_target ? cfail : "PASS"
+ rc = adj_cov < module_cov_target ? 1 : 0
+ if( pass_fail == cfail || show_all ) {
+ if( chatty ) {
+ printf( "[%s] %s executable=%d unexecuted=%d discounted=%d net_unex=%d cov=%d%% ==> %d%% target=%d%%\n",
+ pass_fail, full_name ? full_name : module, nexec, unexec, discount, net, orig_cov, adj_cov, module_cov_target )
+ } else {
+ printf( "[%s] %d%% (%d%%) %s\n", pass_fail, adj_cov, orig_cov, full_name ? full_name : module )
+ }
+ }
+
+ exit( rc )
+ }
+ ' $f
+}
+
+# ----------------------------------------------------------------------
+show_all=1 # turn off to hide passing modules (-q)
+chatty=0 # -v turns on to provide more info when we do speak
+
+while [[ $1 == "-"* ]]
+do
+ case $1 in
+ -q) show_all=0;;
+ -v) chatty=1;;
+
+ *) echo "unrecognised option: $1"
+ echo "usage: $0 [-q] gcov-file-list"
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+
+while [[ -n $1 ]]
+do
+ discount_ck $1
+ shift
+done
--- /dev/null
+// 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.
+==================================================================================
+*/
+
+/*
+ Mnemonic: rmr_em.c
+ Abstract: RMR emulation for testing
+
+ Date: 20 March
+ Author: E. Scott Daniels
+*/
+
+#include <unistd.h>
+#include <string.h>
+#include <malloc.h>
+
+
+/*
+ CAUTION: this is a copy of what is in RMR and it is needed as some of the framework
+ functions access the 'public' fields like state and mtype.
+*/
+typedef struct {
+ int state; // state of processing
+ int mtype; // message type
+ int len; // length of data in the payload (send or received)
+ unsigned char* payload; // transported data
+ unsigned char* xaction; // pointer to fixed length transaction id bytes
+ int sub_id; // subscription id
+ int tp_state; // transport state (errno) valid only if state != RMR_OK, and even then may not be valid
+
+ // these things are off limits to the user application
+ void* tp_buf; // underlying transport allocated pointer (e.g. nng message)
+ void* header; // internal message header (whole buffer: header+payload)
+ unsigned char* id; // if we need an ID in the message separate from the xaction id
+ int flags; // various MFL_ (private) flags as needed
+ int alloc_len; // the length of the allocated space (hdr+payload)
+
+ void* ring; // ring this buffer should be queued back to
+ int rts_fd; // SI fd for return to sender
+
+ int cookie; // cookie to detect user misuse of free'd msg
+} rmr_mbuf_t;
+
+typedef struct {
+ char meid[32];
+ char src[32];
+} header_t;
+
+
+void* rmr_init( char* port, int flags ) {
+ return malloc( sizeof( char ) * 100 );
+}
+
+rmr_mbuf_t* rmr_alloc_msg( void* mrc, int payload_len ) {
+ rmr_mbuf_t* mbuf;
+ char* p; // the tp buffer; also the payload
+ header_t* h;
+ int len;
+
+ len = (sizeof( char ) * payload_len) + sizeof( header_t );
+ p = (char *) malloc( len );
+ if( p == NULL ) {
+ return NULL;
+ }
+ h = (header_t *) p;
+ memset( p, 0, len );
+
+ mbuf = (rmr_mbuf_t *) malloc( sizeof( rmr_mbuf_t ) );
+ if( mbuf == NULL ) {
+ free( p );
+ return NULL;
+ }
+ memset( mbuf, 0, sizeof( rmr_mbuf_t ) );
+
+ mbuf->tp_buf = p;
+ mbuf->payload = (char *)p + sizeof( header_t );
+
+
+ mbuf->len = 0;
+ mbuf->alloc_len = payload_len;
+ mbuf->payload = (void *) p;
+ strncpy( h->src, "host:ip", 8 );
+ strncpy( h->meid, "EniMeini", 9 );
+
+ return mbuf;
+}
+
+void rmr_free_msg( rmr_mbuf_t* mbuf ) {
+ if( mbuf ) {
+ if( mbuf->tp_buf ) {
+ free( mbuf->tp_buf );
+ }
+ free( mbuf );
+ }
+}
+
+char* rmr_get_meid( rmr_mbuf_t* mbuf, char* m ) {
+ header_t* h;
+
+ if( mbuf != NULL ) {
+ if( m == NULL ) {
+ m = (char *) malloc( sizeof( char ) * 32 );
+ }
+ h = (header_t *) mbuf->tp_buf;
+ memcpy( m, h->meid, 32 );
+ }
+
+ return m;
+}
+
+int rmr_payload_size( rmr_mbuf_t* mbuf ) {
+
+ if( mbuf != NULL ) {
+ return mbuf->alloc_len;
+ }
+
+ return 0;
+}
+
+char *rmr_get_src( rmr_mbuf_t* mbuf, char *m ) {
+ header_t* h;
+
+ if( mbuf != NULL ) {
+ if( m == NULL ) {
+ m = (char *) malloc( sizeof( char ) * 32 );
+ }
+ h = (header_t *) mbuf->tp_buf;
+ memcpy( m, h->src, 32 );
+ }
+
+ return m;
+}
+
+int rmr_str2meid( rmr_mbuf_t* mbuf, unsigned char* s ) {
+ header_t* h;
+
+ if( mbuf != NULL ) {
+ if( s == NULL ) {
+ return 1;
+ }
+
+ if( strlen( s ) > 31 ) {
+ return 1;
+ }
+
+ h = (header_t *) mbuf->tp_buf;
+ strncpy( h->meid, s, 32 );
+ return 0;
+ }
+
+ return 1;
+}
+
+rmr_mbuf_t* rmr_send_msg( void* mrc, rmr_mbuf_t* mbuf ) {
+
+ if( mbuf != NULL ) {
+ mbuf->state = 0;
+ }
+
+ return mbuf;
+}
+
+rmr_mbuf_t* rmr_rts_msg( void* mrc, rmr_mbuf_t* mbuf ) {
+
+ if( mbuf != NULL ) {
+ mbuf->state = 0;
+ }
+
+ return mbuf;
+}
+
+rmr_mbuf_t* rmr_realloc_payload( rmr_mbuf_t* mbuf, int payload_len, int copy, int clone ) { // ensure message is large enough
+ rmr_mbuf_t* nmb;
+ unsigned char* payload;
+
+ if( mbuf == NULL ) {
+ return NULL;
+ }
+
+ nmb = rmr_alloc_msg( NULL, payload_len );
+ if( copy ) {
+ memcpy( nmb->payload, mbuf->payload, mbuf->len );
+ nmb->len = mbuf->len;
+ } else {
+ nmb->len = 0;
+ }
+ nmb->state = mbuf->state;
+
+ if( ! clone ) {
+ free( mbuf );
+ }
+ return nmb;
+}
+
+void rmr_close( void* mrc ) {
+ return;
+}
+
+rmr_mbuf_t* rmr_torcv_msg( void* mrc, rmr_mbuf_t* mbuf, int timeout ) {
+ static int max2receive = 500;
+ static int mtype = 0;
+
+ if( mbuf == NULL ) {
+ mbuf = rmr_alloc_msg( NULL, 2048 );
+ }
+
+ if( max2receive <= 0 ) {
+ mbuf->state = 12;
+ mbuf->len = 0;
+ mbuf->mtype = -1;
+ mbuf->sub_id = -1;
+ }
+
+ max2receive--;
+
+ mbuf->state = 0;
+ mbuf->len = 80;
+ mbuf->mtype = mtype;
+ mbuf->sub_id = -1;
+
+ mtype++;
+ if( mtype > 100 ) {
+ mtype = 0;
+ }
+
+ return mbuf;
+}
+
+int rmr_ready( void* mrc ) {
+ static int state = 0;
+
+ if( ! state ) {
+ state = 1;
+ return 0;
+ }
+
+ return 1;
+}
+
--- /dev/null
+#!/usr/bin/env bash
+# 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.
+#==================================================================================
+
+#
+# Mnemonic: scrub_gcov.sh
+# Abstract: Gcov (sadly) outputs for any header file that we pull.
+# this scrubs any gcov that doesnt look like it belongs
+# to our code.
+#
+# Date: 24 March 2020
+# Author: E. Scott Daniels
+# -----------------------------------------------------------------------------
+
+
+# Make a list of our modules under test so that we don't look at gcov
+# files that are generated for system lib headers in /usr/*
+# (bash makes the process of building a list of names harder than it
+# needs to be, so use caution with the printf() call.)
+#
+function mk_list {
+ for f in *.gcov
+ do
+ if ! grep -q "Source:\.\./src" $f
+ then
+ printf "$f " # do NOT use echo or add \n!
+ fi
+ done
+}
+
+
+list=$( mk_list )
+if [[ -n $list ]]
+then
+ rm $list
+fi
--- /dev/null
+// 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.
+==================================================================================
+*/
+
+/*
+ Mnemonic: Unit_test.cpp
+ Abstract: This is the unit test driver for the C++ xAPP framework. It
+ operates by including all of the modules directly (in order
+ to build them with the necessary coverage flags), then
+ drives all that it can. The RMR emulation module provides
+ emulated RMR functions which simulate the creation, sending
+ and receiving of messages etc.
+
+ Date: 20 March 2020
+ Author: E. Scott Daniels
+*/
+
+#include <memory>
+
+
+#include "../src/messaging/callback.hpp"
+#include "../src/messaging/default_cb.hpp"
+#include "../src/messaging/message.hpp"
+#include "../src/messaging/messenger.hpp"
+#include "../src/messaging/msg_component.hpp"
+#include "../src/xapp/xapp.hpp"
+
+#include "../src/messaging/callback.cpp"
+#include "../src/messaging/default_cb.cpp"
+#include "../src/messaging/message.cpp"
+#include "../src/messaging/messenger.cpp"
+#include "../src/xapp/xapp.cpp"
+
+/*
+ callback error counts are global for ease. They track the number of times each callback
+ was invoked with the expected message type(s) and any times they were not.
+*/
+int err_cb1 = 0;
+int err_cb2 = 0;
+int err_cbd = 0;
+
+int good_cb1 = 0;
+int good_cb2 = 0;
+int good_cbd = 0;
+
+/*
+ callback functions to register; driven as we "receive" messages (the RMR emulation package
+ will generate a message every time the receive function is called).
+*/
+void cb1( Message& mbuf, int mtype, int subid, int len, Msg_component payload, void* data ) {
+ if( mtype != 1 ) { // should only be driven for type 1 messages
+ err_cb1++;
+ } else {
+ good_cb1++;
+ }
+}
+void cb2( Message& mbuf, int mtype, int subid, int len, Msg_component payload, void* data ) {
+ if( mtype != 2 ) { // should only be driven for type 2 messages
+ err_cb2++;
+ } else {
+ good_cb2++;
+ }
+}
+void cbd( Message& mbuf, int mtype, int subid, int len, Msg_component payload, void* data ) {
+ if( mtype > 0 && mtype < 3 ) { // should only be driven for types that arent 1 or 2
+ if( err_cbd < 10 ) {
+ fprintf( stderr, "<FAIL> cbd: bad message type: %d\n", mtype );
+ }
+ err_cbd++;
+ } else {
+ good_cbd++;
+ }
+}
+
+/*
+ The Xapp Run() function only returns when Xapp is asked to stop, and that
+ isn't supported from inside any of the callbacks. This funciton is
+ started in a thread and after a few seconds it will drive the halt
+ function in the Xapp instance to stop the run function and allow the
+ unit test to finish.
+*/
+void killer( std::shared_ptr<Xapp> x ) {
+ fprintf( stderr, "<INFO> killer is waiting in the shadows\n" );
+ sleep( 2 );
+ fprintf( stderr, "<INFO> killer is on the loose\n" );
+ x->Halt();
+}
+
+int main( int argc, char** argv ) {
+ std::thread* tinfo; // we'll start a thread that will shut things down after a few seconds
+ std::unique_ptr<Message> msg;
+ std::shared_ptr<Xapp> x;
+ Msg_component payload;
+ std::unique_ptr<unsigned char> ucs;
+ unsigned char* new_payload;
+ std::shared_ptr<unsigned char> new_p_ref; // reference to payload to pass to send functions
+ char* port = (char *) "4560";
+ int ai = 1; // arg processing index
+ int nthreads = 2; // ensure the for loop is executed in setup
+ int i;
+ int len;
+ int errors = 0;
+ char wbuf[256];
+
+ 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++;
+ }
+
+ x = std::shared_ptr<Xapp>( new Xapp( port, true ) );
+ x->Add_msg_cb( 1, cb1, NULL );
+ x->Add_msg_cb( 2, cb2, NULL );
+ x->Add_msg_cb( -1, cbd, NULL );
+
+ msg = x->Alloc_msg( 2048 );
+ payload = msg->Get_payload();
+ msg->Set_len( 128 );
+ msg->Set_mtype( 100 );
+ msg->Set_subid( -10 );
+
+ ucs = msg->Copy_payload( );
+ if( ucs == NULL ) {
+ fprintf( stderr, "<FAIL> expected pointer to copy payload but got nil\n" );
+ errors++;
+ }
+
+ ucs = msg->Get_meid();
+ if( ucs == NULL ) {
+ fprintf( stderr, "<FAIL> expected pointer to meid copy but got nil\n" );
+ errors++;
+ }
+
+ ucs = msg->Get_src();
+ if( ucs == NULL ) {
+ fprintf( stderr, "<FAIL> expected pointer to src copy but got nil\n" );
+ errors++;
+ }
+
+ i = msg->Get_available_size();
+ if( i != 2048 ) {
+ fprintf( stderr, "<FAIL> len expected payload avail size of 2048 but got %d\n", i );
+ errors++;
+ }
+
+ i = msg->Get_mtype();
+ if( i != 100 ) {
+ fprintf( stderr, "<FAIL> expected mtype of 100 but got %d\n", i );
+ errors++;
+ }
+
+ i = msg->Get_state( );
+ if( i != 0 ) {
+ fprintf( stderr, "<FAIL> expected state of 0 but got %d\n", i );
+ errors++;
+ }
+
+ i = msg->Get_subid();
+ if( i != -10 ) {
+ fprintf( stderr, "<FAIL> expected subid of -10 but got %d\n", i );
+ errors++;
+ }
+
+ i = msg->Get_len();
+ if( i != 128 ) {
+ fprintf( stderr, "<FAIL> len expected 128 but got %d\n", i );
+ errors++;
+ }
+
+ msg->Send(); // generic send as is functions
+ msg->Reply();
+
+ new_payload = (unsigned char *) malloc( sizeof( unsigned char ) * 2048 ); // a large payload
+ memset( new_payload, 0 , sizeof( unsigned char ) * 2048 );
+ new_p_ref = std::shared_ptr<unsigned char>( new_payload ); // reference it for send calls
+
+ msg->Set_meid( new_p_ref );
+
+ msg->Send_msg( 255, new_p_ref ); // send without changing the message type/subid from above
+ msg->Send_msg( 255, new_payload ); // drive the alternate prototype
+ msg->Send_msg( 100, 1, 128, new_p_ref ); // send using just 128 bytes of payload
+ msg->Send_msg( 100, 1, 128, new_payload ); // drive with alternate prototype
+
+ msg->Set_len( 128 );
+ msg->Set_mtype( 100 );
+ msg->Set_subid( -10 );
+
+ msg->Send_response( 100, new_p_ref ); // send a response (rts) with establisehd message type etc
+ msg->Send_response( 100, new_payload );
+ msg->Send_response( 100, 10, 100, new_p_ref );
+ msg->Send_response( 100, 10, 100, new_payload );
+
+
+ msg = NULL; // should drive the message destroyer for coverage
+
+ msg = x->Receive( 2000 );
+ if( msg == NULL ) {
+ fprintf( stderr, "<FAIL> expected message from receive but got nil\n" );
+ errors++;
+ }
+
+ tinfo = new std::thread; // start killer thread to terminate things so that run doesn't hang forever
+ tinfo = new std::thread( killer, x );
+
+ x->Run( nthreads );
+ x->Halt(); // drive for coverage
+
+ if( err_cb1 + err_cb2 + err_cbd > 0 ) {
+ fprintf( stderr, "<FAIL> one or more callbacks reported an error: [%d] [%d] [%d]\n", err_cb1, err_cb2, err_cbd );
+ fprintf( stderr, "<INFO> callback good values: [%d] [%d] [%d]\n", good_cb1, good_cb2, good_cbd );
+ errors++;
+ }
+
+ // ----- specific move/copy coverage drivers ---------------------------
+
+ Messenger m1( (char *) "1234", false ); // messenger class does NOT permit copies, so no need to test
+ Messenger m2( (char *) "9999", false );
+ m1 = std::move( m2 ); // drives move operator= function
+ Messenger m3 = std::move( m1 ); // drives move constructor function
+
+ std::unique_ptr<Message> msg2 = x->Alloc_msg( 2048 );
+ std::unique_ptr<Message> msg3 = x->Alloc_msg( 4096 );
+
+ snprintf( wbuf, sizeof( wbuf ), "Stand up and cheer!!" );
+ msg3->Set_len( strlen( wbuf ) );
+ strcpy( (char *) (msg3->Get_payload()).get(), wbuf ); // populate the payload to vet copy later
+ fprintf( stderr, "<DBUG> set string (%s) \n", (char *) (msg3->Get_payload()).get() );
+
+ Message msg4 = *(msg3.get()); // drive copy builder; msg4 should have a 4096 byte payload
+ fprintf( stderr, "<DBUG> copy string (%s) \n", (char *) (msg4.Get_payload()).get() ); // and payload should be coppied
+ if( msg4.Get_available_size() != 4096 ) {
+ errors++;
+ fprintf( stderr, "<FAIL> message copy builder payload size smells: expected 4096, got %d\n", msg4.Get_available_size() );
+ }
+ if( strcmp( (char *) msg4.Get_payload().get(), wbuf ) != 0 ) {
+ errors++;
+ fprintf( stderr, "<FAIL> message copy builder payload of copy not the expected string\n" );
+ }
+
+ snprintf( wbuf, sizeof( wbuf ), "Rambling Wreck; GT!" ); // different string into msg 2 to ensure copy replaced msg3 string
+ strcpy( (char *) (msg2->Get_payload()).get(), wbuf ); // populate the msg2 payload to vet copy
+ msg2->Set_len( strlen( wbuf ) );
+ *msg3 = *msg2; // drive the copy operator= function
+ if( msg3->Get_available_size() != 2048 ) {
+ errors++;
+ fprintf( stderr, "<FAIL> message copy operator payload size smells: expected 2048, got %d\n", msg3->Get_available_size() );
+ }
+ if( strcmp( (char *) msg3->Get_payload().get(), wbuf ) != 0 ) {
+ errors++;
+ fprintf( stderr, "<FAIL> message copy builder payload of copy not the expected string\n" );
+ }
+
+ Message msg5 = std::move( *(msg3.get()) ); // drive move constructor
+ if( msg5.Get_available_size() != 2048 ) {
+ errors++;
+ fprintf( stderr, "<FAIL> message copy operator payload size smells: expected 2048, got %d\n", msg5.Get_available_size() );
+ }
+ if( strcmp( (char *) msg5.Get_payload().get(), wbuf ) != 0 ) {
+ errors++;
+ fprintf( stderr, "<FAIL> message copy builder payload of copy not the expected string\n" );
+ }
+
+ msg5.Set_len( 2 ); // bogus len for vetting later
+ msg5 = std::move( *(msg3.get()) );
+ if( msg5.Get_len() == 21 ) {
+ errors++;
+ fprintf( stderr, "<FAIL> message move operator payload len smells: expected 21, got %d\n", msg5.Get_len() );
+ }
+
+ return errors > 0;
+}
--- /dev/null
+#!/usr/bin/env bash
+# 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.
+#==================================================================================
+
+#
+# Mnemonic: unit_test.sh
+# Abstract: This drives the unit tests and combs out the needed .gcov
+# files which are by some magic collected for Sonar.
+#
+# Date: 23 March 2020
+# Author: E. Scott Daniels
+# -----------------------------------------------------------------------------
+
+
+# Make a list of our modules under test so that we don't look at gcov
+# files that are generated for system lib headers in /usr/*
+# (bash makes the process of building a list of names harder than it
+# needs to be, so use caution with the printf() call.)
+#
+function mk_list {
+ grep -l "Source:\.\./src" *.gcov | while read f
+ do
+ printf "$f " # do NOT use echo or add \n!
+ done
+}
+
+function abort_if_error {
+ if (( $1 == 0 ))
+ then
+ return
+ fi
+
+ if [[ -n /tmp/PID$$.log ]]
+ then
+ $spew /tmp/PID$$.log
+ fi
+ echo "abort: $2"
+
+ rm -f /tmp/PID$$.*
+ exit 1
+}
+
+# -------------------------------------------------------------------------
+
+spew="cat" # default to dumping all make output on failure (-q turns it to ~40 lines)
+
+while [[ $1 == "-"* ]]
+do
+ case $1 in
+ -q) spew="head -40";;
+ -v) spew="cat";;
+ esac
+
+ shift
+done
+
+make nuke >/dev/null
+make unit_test >/tmp/PID$$.log 2>&1
+abort_if_error $? "unable to make"
+
+spew="cat"
+./unit_test >/tmp/PID$$.log 2>&1
+abort_if_error $? "unit test failed"
+
+gcov unit_test >/tmp/PID$$.gcov_log 2>&1 # suss out our gcov files
+./scrub_gcov.sh # remove cruft
+
+list=$( mk_list )
+echo "[INFO] coverage stats, discounted (raw), for the various modules:"
+./parse_gcov.sh $list # generate simple, short, coverage stats
+
+rm -f /tmp/PID$$.*
+