From 6ef23e1d7f7f47ccab5be6ff1b68e5cfdda80ad2 Mon Sep 17 00:00:00 2001 From: "E. Scott Daniels" Date: Wed, 15 Jul 2020 08:03:22 -0400 Subject: [PATCH] Add API allowing xAPPs to send alarm messages This change adds the ability for an xAPP to send alarms to the alarm collector/manager via RMR messages. This is a BREAKING change because the C++ framework classes are all now wrapped in the xapp:: namespace. Issue-ID: RIC-426 Signed-off-by: E. Scott Daniels Change-Id: I552292d57e987313ec684bd55f8780ef5fba74fe --- CHANGES | 15 + CMakeLists.txt | 21 +- doc/src/lib/README | 4 + doc/src/lib/front_junk.im | 21 +- doc/src/lib/setup.im | 75 ++- doc/src/master.mk | 41 ++ doc/src/rtd/Makefile | 22 +- doc/src/rtd/gen_rel_notes.sh | 14 +- doc/src/rtd/rel-notes.rst | 27 +- doc/src/rtd/rel-notes.xfm | 37 - doc/src/rtd/setup.im | 83 --- doc/src/user/.gitignore | 10 + doc/src/user/Makefile | 27 +- doc/src/user/alarm.im | 196 ++++++ doc/src/user/alloc_proto.im | 51 ++ doc/src/user/cpp_frame.im | 40 +- doc/src/user/example4.im | 37 + doc/src/user/jhash.im | 52 +- doc/src/user/raise_proto.im | 57 ++ doc/src/user/user_guide.xfm | 2 + docs/rel-notes.rst | 27 +- docs/user-guide.rst | 1422 ++++++++++++++++++++++++--------------- examples/rmr_dump.cpp | 14 +- examples/xapp_t1.cpp | 12 +- examples/xapp_t2.cpp | 6 +- src/alarm/CMakeLists.txt | 40 ++ src/alarm/alarm.cpp | 460 +++++++++++++ src/alarm/alarm.hpp | 111 +++ src/json/jhash.cpp | 55 +- src/json/jhash.hpp | 4 + src/messaging/callback.cpp | 6 +- src/messaging/callback.hpp | 10 +- src/messaging/default_cb.cpp | 6 + src/messaging/default_cb.hpp | 4 + src/messaging/message.cpp | 120 ++-- src/messaging/message.hpp | 9 +- src/messaging/messenger.cpp | 86 ++- src/messaging/messenger.hpp | 17 +- src/messaging/msg_component.hpp | 4 + src/xapp/xapp.hpp | 2 +- test/Makefile | 2 +- test/jhash_test.cpp | 16 +- test/rmr_em.c | 51 +- test/unit_test.cpp | 107 ++- 44 files changed, 2456 insertions(+), 967 deletions(-) create mode 100644 doc/src/lib/README create mode 100644 doc/src/master.mk delete mode 100644 doc/src/rtd/rel-notes.xfm delete mode 100644 doc/src/rtd/setup.im create mode 100644 doc/src/user/alarm.im create mode 100644 doc/src/user/alloc_proto.im create mode 100644 doc/src/user/example4.im create mode 100644 doc/src/user/raise_proto.im create mode 100644 src/alarm/CMakeLists.txt create mode 100644 src/alarm/alarm.cpp create mode 100644 src/alarm/alarm.hpp diff --git a/CHANGES b/CHANGES index d7bd724..b856218 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,17 @@ # This file contains a brief summary of each version bump. +# Its contents are parsed by a document builder and the rel-notes.rst +# file for the RTD site is generated. Changes must start with a date +# and version string. Blank are used to separate items when a version +# has several changes. Multiple blank lines between versions are +# squished to one. + +release = Cherry +2020 July 17; version 2.0.0 + Add alarm support. + + Add xapp namespace for Message, Msg_component, Alarm and Jhah + objects. This is a BREAKING change and thus the major version + number was bumped to 2. 2020 June 29; version 1.2.0 Add support for json parsing @@ -9,6 +22,8 @@ Version bump to 1.1.0 to allow patches to bronze code to continue to be done on the 1.0.* level. + +release= Bronze 2020 April 28; version 1.0.0 Bump version to force package build (old CI version added incorrect install prefix). Bump to 1.0.0 for release. diff --git a/CMakeLists.txt b/CMakeLists.txt index fc913fa..949b158 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,9 +28,9 @@ project( ricxfcpp ) cmake_minimum_required( VERSION 3.5 ) -set( major_version "1" ) # should be automatically populated from git tag later, but until CI process sets a tag we use this -set( minor_version "2" ) -set( patch_level "1" ) +set( major_version "2" ) # should be automatically populated from git tag later, but until CI process sets a tag we use this +set( minor_version "0" ) +set( patch_level "0" ) set( install_root "${CMAKE_INSTALL_PREFIX}" ) set( install_inc "include/ricxfcpp" ) @@ -149,17 +149,21 @@ endif() +# bleeding cmake names are short novels; and when lines cannot be split they're a pain +set ( srcd "${CMAKE_CURRENT_SOURCE_DIR}" ) + # 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;${CMAKE_CURRENT_SOURCE_DIR}/src/json;${CMAKE_CURRENT_SOURCE_DIR}/ext/jsmn" ) +include_directories( "${srcd}/src/messaging;${srcd}/src/json;${srcd}/src/alarm;${srcd}/ext/jsmn" ) # Compiler flags # set( CMAKE_POSITION_INDEPENDENT_CODE ON ) set( CMAKE_C_FLAGS "-g " ) set( CMAKE_CPP_FLAGS "-g " ) +set( CMAKE_CXX_FLAGS "-g " ) if( GPROF ) # if set, we'll set profiling flag on compiles message( "+++ profiling is on" ) set( CMAKE_C_FLAGS "-pg " ) @@ -181,12 +185,13 @@ execute_process( COMMAND git submodule update --init -- ext/jsmn add_subdirectory( src/json ) add_subdirectory( src/messaging ) add_subdirectory( src/xapp ) +add_subdirectory( src/alarm ) #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 - "$;$;$" + "$;$;$;$" ) set_target_properties( ricxfcpp_shared PROPERTIES @@ -232,9 +237,9 @@ else() endif() install( TARGETS ${target_list} EXPORT LibraryConfig - LIBRARY DESTINATION ${install_lib} - ARCHIVE DESTINATION ${install_lib} - PUBLIC_HEADER DESTINATION ${install_inc} + 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 diff --git a/doc/src/lib/README b/doc/src/lib/README new file mode 100644 index 0000000..559e586 --- /dev/null +++ b/doc/src/lib/README @@ -0,0 +1,4 @@ + +This directory contains imbed files (headers) for the various +documentation source in other directories. These imbed files +provide common macros, variables, etc. diff --git a/doc/src/lib/front_junk.im b/doc/src/lib/front_junk.im index 1109537..0d4b41a 100644 --- a/doc/src/lib/front_junk.im +++ b/doc/src/lib/front_junk.im @@ -29,12 +29,18 @@ doc_subtitle orig_date + Setup.im must be embedded first, or &lib must be defined + by the doc which imbeds this file. + Date: 21 April 2020 .fi .if ! _front_junk .gv e OUTPUT_TYPE ot .if "&ot" "generic_ps" = + .cd 1 6.5i m=1i + + .sf &{textfont!Helvetica} .sp 20 .st 24 .bc start @@ -75,7 +81,7 @@ .** ensure these happen after toc is rendered .if pfm .pn on 0 noline center f=%d - .cd 1 180i + .cd 1 6.5i .fi .ei .if "&ot" "rst" = @@ -85,19 +91,6 @@ .im &{lib}/raw_license.im .cd 1 6.5i m=0i .ll 6i - -.if false - .if doc_title - &many_equals - &doc_title - &many_equals - .fi - .if doc_subtitle - &many_dashes - &doc_subtitle - &many_dashes - .fi -.fi .fi .dv _front_junk 1 diff --git a/doc/src/lib/setup.im b/doc/src/lib/setup.im index c04a8ca..6d51a00 100644 --- a/doc/src/lib/setup.im +++ b/doc/src/lib/setup.im @@ -19,54 +19,82 @@ .if false Mnemonic: setup.im - Abstract: Look at environment variables and pull in the correct setup prep - imbed file based on the desired output type (when running tfm). - Obviously, when running pfm we are always generating postscript - so this isn't really doing much. + Abstract: Provide general setup for all documents. It will include the + {X}fm common macro definitions (cmd) which provide the ability + to generate markdown, roff, and RST "source" from {X}fm source + while still allowing postscript to be generated. + + Visit gitlab.com/rouxware/xfm for info on how to bulid + {X}fm if needed. + Date: 29 July 2019 .fi .if ! _setup_im - .gv e XFM_IM_LIB lib - .if ! lib - .dv lib ../lib - .fi + .dv _setup_im 1 + + .** our imbeds live here + .dv lib ../lib .** sane default for everything except rst .dv col_width 6.5i .dv textsize 10 .dv textfont Helvetica + .hn off + .tm .75i .** imbed output type specific macro file .gv e OUTPUT_TYPE ot .dv output_type &{ot!txt} + .** {X}fm package provided common macro definitions .im cmd_master.im - .** .im &{lib}/&{ot}.im .gv e XFM_PASS pass .dv pass &{pass!1} - .** define a 'subroutine' to add a variable and place it into the forward reference file - .** fref_sub.ca contains code to execute by the set_fref macor and fref.ca contains the - .** forward references to include during pass2. The macro set_ref will define the variable - .** passed as $1 (name) with the value passed as $2 and will add it to the fref.ca file. + .** check that current version of {X}fm is greater/equal to our minimum req'd + .if ! [ "2.3.2" CVGE ] + .gv + .mg ### WARN ### this document source expects {X}fm version 2.3.2 or greater; found &{_major}.&{_minor}.&_patch + .fi + + .gv e XFM_PATH xpath + .if ! xpath + .gv e TFM_PATH xpath + .if ! xpath + .mg ### WARNING ### XFM_PATH should be set to reference the shared imbed files; likely these are in /usr/local/share/xfm + .fi + .fi + + .** forward variable reference support + .** define a 'subroutine' to add a variable and place it into the forward reference file + .** fref_sub.ca contains code to execute by the set_fref macor and fref.ca contains the + .** forward references to include during pass2. The macro set_ref will define the variable + .** passed as $1 (name) with the value passed as $2 and will add it to the fref.ca file. .** -.ca shift start fref_sub.ca - .ca expand extend fref.ca - .dv &vname &vval - .ca end + .ca shift start _fref_sub.ca + .** be careful... the next 'ca end' MUST be tab indented before {X} version 2.4. + .** with 2.4 and after it can be space indented + + .ca expand extend _fref.ca + .dv &vname &vval + .ca end +.** this ca end MUST be in col 0 .ca end - .dv set_fref .dv vname $1 ^: .dv vval $2 ^: .dv $1 $2 ^: .im fref_sub.ca + .dv export_var .dv vname $1 ^: .dv vval $2 ^: .dv $1 $2 ^: .im _fref_sub.ca + .dv export_fig .dv vname $1 ^: .gv fig .ev ^`.dv vval ^^&_fig ^: ^` .im _fref_sub.ca + .dv export_tab .dv vname $1 ^: .gv table .ev ^`.dv vval ^^&_table ^` ^: .im _fref_sub.ca + .dv export_pg .dv vname $1 ^: .gv page .ev ^`.dv vval ^^&_page ^` ^: .im _fref_sub.ca .if &pass 2 = - .im fref.ca + .im _fref.ca .ei .** initialise the forward ref file - .ca start fref.ca + .ca start _fref.ca .** forward reference variables; auto generated .ca end .fi @@ -74,6 +102,11 @@ .** set up for an index when using pfm and snare file is defined .if pfm + .** {X}fm macro definitions are too big + .dh 1 p=14 s=2,1 e=off + .dh 2 p=12 s=1,.5 + .dh 3 p=&{textsize!10} s=1,0 + .if index_snare_file .ix space .sp 1 .cc 5 %c .ln ^: .sp 0 .ix term .br %s `^` ^`` %d @@ -89,6 +122,4 @@ .fi .fi - .dv _setup_im 1 - .fi diff --git a/doc/src/master.mk b/doc/src/master.mk new file mode 100644 index 0000000..b4e252c --- /dev/null +++ b/doc/src/master.mk @@ -0,0 +1,41 @@ +#================================================================================== +# 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. +#================================================================================== + +# common make meta rules and vars for all subdirectories + +XPATH=XFM_PATH=.:/usr/local/share/xfm TFM_PATH=.:/usr/local/share/xfm + +# use care: the output type is used to source the macros based on the type +# of output being rendered. +# Two pass builds allow for table of contents gen/insert, forward var +# references etc. +# +%.ps: %.xfm + $(XPATH) OUTPUT_TYPE=generic_ps XFM_PASS=1 pfm $< /dev/null + $(XPATH) OUTPUT_TYPE=generic_ps XFM_PASS=2 pfm $< $@ + +%.md: %.xfm + $(XPATH) OUTPUT_TYPE=markdown XFM_PASS=1 tfm $< /dev/null + $(XPATH) OUTPUT_TYPE=markdown XFM_PASS=2 tfm $< | sed 's/^ //' >$@ + +%.rst: %.xfm + $(XPATH) OUTPUT_TYPE=rst XFM_PASS=1 tfm $< /dev/null + $(XPATH) OUTPUT_TYPE=rst XFM_PASS=2 tfm $< | sed 's/^ //; s/ *$$//' >$@ + +%.txt: %.xfm + $(XPATH) OUTPUT_TYPE=txt XFM_PASS=1 tfm $< /dev/null + $(XPATH) OUTPUT_TYPE=txt XFM_PASS=2 tfm $< $@ diff --git a/doc/src/rtd/Makefile b/doc/src/rtd/Makefile index eda0613..716358c 100644 --- a/doc/src/rtd/Makefile +++ b/doc/src/rtd/Makefile @@ -22,26 +22,9 @@ src = rel-notes.xfm imbed_src = desired_out = rst -# use care: the output type is used to source the macros based on the type -# of output being rendered. -# Two pass builds allow for table of contents gen/insert, forward var -# references etc. -# -%.ps: %.xfm - OUTPUT_TYPE=generic_ps XFM_PASS=1 pfm $< /dev/null - OUTPUT_TYPE=generic_ps XFM_PASS=2 pfm $< $@ - -%.md: %.xfm - OUTPUT_TYPE=markdown XFM_PASS=1 tfm $< /dev/null - OUTPUT_TYPE=markdown XFM_PASS=2 tfm $< | sed 's/^ //' >$@ - -%.rst: %.xfm - OUTPUT_TYPE=rst XFM_PASS=1 tfm $< /dev/null - GEN_TITLE=1 OUTPUT_TYPE=rst XFM_PASS=2 tfm $< | sed 's/ $$//; s/^ //' >$@ +include ../master.mk -%.txt: %.xfm - OUTPUT_TYPE=txt XFM_PASS=1 tfm $< /dev/null - OUTPUT_TYPE=txt XFM_PASS=2 tfm $< $@ +XPATH = XFM_PATH=.:/usr/local/share/xfm # ----------------------------------------------------------------------------------- @@ -70,6 +53,7 @@ clean: # Destroy anything that can be built nuke: clean rm -fr *.md *.ps *.pdf *.txt *.rst *.toc + rm -fr rel-notes.xfm # make hack to force a rule to always be out of date always: diff --git a/doc/src/rtd/gen_rel_notes.sh b/doc/src/rtd/gen_rel_notes.sh index eb3b99b..fb19338 100755 --- a/doc/src/rtd/gen_rel_notes.sh +++ b/doc/src/rtd/gen_rel_notes.sh @@ -31,8 +31,10 @@ cat <$@ - -%.rst: %.xfm - $(XPATH) OUTPUT_TYPE=rst XFM_PASS=1 tfm $< /dev/null - $(XPATH) GEN_TITLE=1 OUTPUT_TYPE=rst XFM_PASS=2 tfm $< | sed 's/^ //; s/ *$$//' >$@ +imbed_src = cpp_frame.im example1.im example2.im example3.im jhash.im example4.im +desired_out = rst ps md -%.txt: %.xfm - $(XPATH) OUTPUT_TYPE=txt XFM_PASS=1 tfm $< /dev/null - $(XPATH) OUTPUT_TYPE=txt XFM_PASS=2 tfm $< $@ +include ../master.mk # ----------------------------------------------------------------------------------- all: $(desired_out:%=user_guide.%) diff --git a/doc/src/user/alarm.im b/doc/src/user/alarm.im new file mode 100644 index 0000000..e020a67 --- /dev/null +++ b/doc/src/user/alarm.im @@ -0,0 +1,196 @@ +.** vim: sw=4 ts=4 et : +.if false +================================================================================== + 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. +================================================================================== +.fi + + +.if false + This imbed file contains the portion of the document that describes the + alarm collector AIP provided by the framework. +.fi + + +.if pfm + .** work round bug in cmd_ps.im + .dv beg_table .ta $1 + .dv end_table .et + .dv col .cl ^: + .dv row .tr ^: +.fi + +.** ------ positioning ------------------------------------- + +&h1(Alarm Manager Interface) +The C++ framework provides an API which allows the xAPP to easily construct and +generate alarm messages. +Alarm messages are a special class of RMR message, allocated in a similar fashion +as an RMR message through the framework's &cw(Alloc_alarm()) function. + +&space +The API consists of the following function types: + +&half_space +&indent +&beg_dlist( 1i &ditemtext: ) + &di(Raise) Cause the alarm to be assigned a severity and and sent via RMR + message to the alarm collector process. + + &half_space + &di(Clear) Cause a clear message to be sent to the alarm collector. + + &half_space + &di(Raise Again) Cause a clear followed by a raise message to be sent to + the alarm collector. +&end_dlist +&uindent +&space + +&h2(Allocating Alarms) +The &cw(xapp::Alloc_alarm()) function provided by the framework is used to create +an alarm object. +Once the xAPP has an alarm object it can be used to send one, or more, alarm +messages to the collector. + +&space +The allocation function has three prototypes which allow the xAPP to create +an alarm with an initial set of information as is appropriate. +The following are the prototypes for the allocate functions: + +&half_space +.im alloc_proto.im + + +Each of the allocation functions returns a unique pointer to the alarm. +In the simplest form (1) the alarm is initialised with an empty meid +(managed element ID) string, and unset problem ID (-1). +The second prototype allows the xAPP to supply the meid, and in the +third form both the problem ID and the meid are used to initialise the +alarm. + +&h2(Raising An Alarm) +Once an alarm has been allocated, its &cw(Raise()) function can be used +to cause the alarm to be sent to the collector. +The raise process allows the xAPP to perform the following modifications +to the alarm before sending the message: + +&half_space +&indent +&beg_list(&lic1) + &li Set the alarm severity + &half_space + &li Set the problem ID value + &half_space + &li Set the alarm information string + &half_space + &li Set the additional information string +&end_list +&uindent +&space + +The following are the prototypes for the &cw(Raise()) functions of an alarm object: +..... + + + +In its simplest form (1) the &cw(Raise()) function will send the alarm without making any +changes to the data. +The final two forms allow the xAPP to supply additional data which is added to the +alarm before sending the message. + +Each of the raise functions returns &cw(true) on success and &cw(false) if the alarm +message could not be sent. + +&h3(Severity) +The severity is one of the &cw(SEV_) constants listed below. +These map to alarm collector strings and insulate the xAPP from any future alarm collector +changes. +The specific meaning of these severity types are defined by the alarm collector and thus +no attempt is made to guess what their actual meaning is. +These constants are available by including &cw(alarm.hpp.) + +&half_space +&indent +&ex_start + SEV_MAJOR + SEV_MINOR + SEV_WARN + SEV_DEFAULT +&ex_end +&uindent +&fig_cen(Severity constants available in alarm.hpp.) + +&h3(The Problem ID) +The problem ID is an integer which is assigned by the xAPP. +The framework makes no attempt to verify that it has been se, nor does it attempt +to validate the value. +If the xAPP does not set the value, &cw(-1) is used. + +&h3(Information Strings) +The two information strings are also xAPP defined and provide the information that +the xAPP deems necessary and related to the alarm. +What the collector expects, and how these strings are used, is beyond the scope of +the framework to describe or validate. +If not supplied, empty strings are sent in the alarm message. + +&h2(Clearing An Alarm) +The &cw(Clear()) function of an alarm may be used to send a clear message. +In a manner similar to the &cw(Raise()) functions, the &cw(Clear()) functions allow +the existing alarm data to be sent without change, or for the xAPP to modify the +data before the message is sent to the collector. +The following are the prototype for these functions. + + +&ex_start + bool Clear( ); + bool Clear( int severity, int problem, std::string info ); + bool Clear( int severity, int problem, std::string info, std::string addional_info ); + bool Clear_all( ); + +&ex_end +&fig_cen(Clear function prototypes. ) +&space + +Each of the clear functions returns &cw(true) on success and &cw(false) if the alarm +message could not be sent. + + +&space +The &cw(Clear_all()) function sends a special action code to the collector which is assumed +to clear all alarms. +However, it is unknown whether that implies &bold(all) alarms, or all alarms matching the +&cw(problem_id,) or some other interpretation. +Please consult the alarm collector documentation for these specifics. + + +&h2(Adjusting Alarm Contents) +It might be necessary for the xAPP to adjust the alarm contents outside of the scope of +the &cw(Raise()) function, or to adjust data that cannot be manipulated by &cw(Raise().) +The following are the (self explanatory) prototypes for the &ital(setter) functions +which are available to the xAPP. +&half_space + +&ex_start + void Set_additional( std::string new_info ); + void Set_appid( std::string new_id ); + void Set_info( std::string new_info ); + void Set_meid( std::string new_meid ); + void Set_problem( int new_id ); + void Set_severity( int new_sev ); +&ex_end +&fig_cen(Alarm Setters) +&space diff --git a/doc/src/user/alloc_proto.im b/doc/src/user/alloc_proto.im new file mode 100644 index 0000000..bcb48c8 --- /dev/null +++ b/doc/src/user/alloc_proto.im @@ -0,0 +1,51 @@ +.** vim: sw=4 ts=4 et : +.if false +================================================================================== + 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. +================================================================================== +.fi + + +.** for now we have to sort out based on pfm vs tfm since table +.** constuction for any of the "mark*" parsers sucks. + +.ca start proto.ca +&ex_start + std::unique_ptr Alloc_alarm( ); + std::unique_ptr Alloc_alarm( std::string meid ); + std::unique_ptr Alloc_alarm( int prob_id, std::string meid ); +&ex_end +.ca end + +.if pfm +&beg_table( 5.25i .3i b=0 : 80,15 ) +.im proto.ca +&col +&ex_start +(1) +(2) +(3) +&ex_end +&end_table + +.ei + +.im proto.ca + +.fi + +&fig_cen(Alarm allocation prototypes.) +&space diff --git a/doc/src/user/cpp_frame.im b/doc/src/user/cpp_frame.im index 0c7a560..f9e3963 100644 --- a/doc/src/user/cpp_frame.im +++ b/doc/src/user/cpp_frame.im @@ -34,6 +34,22 @@ the instance of the object. The following paragraphs cover the various steps involved to create an xApp instance, wait for a route table to arrive, send a message, and wait for messages to arrive. +&h2(The Namespace) +Starting with version 2.0.0 the framwork introduces a &ital(namespace) of &cw(xapp) for the following +classes and types: +&half_space +&indent +&beg_list(&lic1) + &li Alarm + &li Jhash + &li Message + &li Msg_component +&end_list +&uindent +&space + +This is a breaking change and as such the major version was bumpped from 1 to 2. + &h2(Creating the xApp instance) The creation of the xApp instance is as simple as invoking the object's constructor with two required parameters: @@ -67,7 +83,7 @@ The following code sample illustrates the simplicity of creating the instance of new Xapp( listen_port, wait4table ) ); } &ex_end -&fig( Creating an xAPP instance.) +&fig_cen( Creating an xAPP instance.) &space From a compilation perspective, the following is the simple compiler invocation string needed to compile @@ -102,11 +118,11 @@ The following is the prototype which callback functions must be defined with: &half_space &ex_start - void cb_name( Message& m, int mtype, int subid, - int payload_len, Msg_component payload, + void cb_name( xapp::Message& m, int mtype, int subid, + int payload_len, xapp::Msg_component payload, void* usr_data ); &ex_end -&fig( Callback function signature) +&fig_cen( Callback function signature) &space The parameters passed to the callback function are as follows: @@ -146,8 +162,8 @@ framework (explained in the next section). long m1001_count = 0; // callback function that will increase the appropriate counter - void cbf( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { + void cbf( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { long* counter; if( (counter = (long *) data) != NULL ) { @@ -170,7 +186,7 @@ framework (explained in the next section). xapp->Run( 1 ); // start the callback driver } &ex_end -&fig( Callback function example.) +&fig_cen( Callback function example.) &space As before, the program does nothing useful, but now it will execute and receive messages. @@ -232,7 +248,7 @@ with the following prototypes. bool Send_response( int response_len, std::shared_ptr response ); &ex_end -&fig( Reply function prototypes. ) +&fig_cen( Reply function prototypes. ) &space In the first prototype the xApp must supply the new message type and subscription ID values, where the @@ -245,10 +261,10 @@ constant can be used as illustrated below. &half_space &ex_start - msg->Send_response( Message::NO_CHANGE, Message::NO_SUBID, + msg->Send_response( xapp::Message::NO_CHANGE, xapp::Message::NO_SUBID, pl_length, (unsigned char *) payload ); &ex_end -&fig( Send response prototype. ) +&fig_cen( Send response prototype. ) &space In addition to the two function prototypes for &cw(Send_response()) there are two additional prototypes @@ -273,7 +289,7 @@ functions and are shown below. bool Send_msg( int payload_len, unsigned char* payload ); &ex_end -&fig( Send function prototypes. ) +&fig_cen( Send function prototypes. ) &space Each send function accepts the message, copies in the payload provided, sets the message type and subscription @@ -314,7 +330,7 @@ for the response string, the response is just not sent). msg->Send_response( M_TYPE, SID, strlen( raw_pl ), NULL ); } &ex_end -&fig( Send message without buffer copy. ) +&fig_cen( Send message without buffer copy. ) &space &h2(Sending Multiple Responses) diff --git a/doc/src/user/example4.im b/doc/src/user/example4.im new file mode 100644 index 0000000..a1968bf --- /dev/null +++ b/doc/src/user/example4.im @@ -0,0 +1,37 @@ +.if false +================================================================================== + 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. +================================================================================== +.fi + +&h2(Alarm Example) +This is an extension of a previous example which sends an alarm +during initialisation and clears the alarm as soon as messages +are being received. +It is unknown if this is the type of alarm that is expected at the +collector, but illustrates how an alarm is allocated, raised and +cleared. + + +&space +&indent +.** pull in the code from the example directory +&ex_start +.im j=start-example ../../../examples/xapp_alarm.cpp +&ex_end +&fig( Simple looping sender application with alarm generation. ) +&space + diff --git a/doc/src/user/jhash.im b/doc/src/user/jhash.im index ec30b0c..66bbb57 100644 --- a/doc/src/user/jhash.im +++ b/doc/src/user/jhash.im @@ -46,7 +46,7 @@ The Jhash object is created simply by passing a json string to the constructor. jh = new Jhash( jstring.c_str() ); &ex_end -&fig(The creation of the Jhash object.) +&fig_cen(The creation of the Jhash object.) &space @@ -59,9 +59,11 @@ The approach taken by Jhash is a "directory view" approach, where the "current d or current &ital(blob,) limits the scope of visible fields. &space -As an example, the json contained in figure jblob_fig, contains a "root" blob and +As an example, the json contained in figure &jblob_fig, contains a "root" blob and two &ital(sub-blobs) (address and lease_info). +&half_space +.ca start blob_ex.ca &ex_start { "lodge_name": "Water Buffalo Lodge 714", @@ -82,26 +84,26 @@ two &ital(sub-blobs) (address and lease_info). } } &ex_end -.gv fig -.sv _fig -&set_fref(jblob_fig:&_fig) -&fig(Sample json with a root and too blobs.) - +&export_fig( jblob_fig ) +&fig_cen(Sample json with a root and two blobs.) &space +.ca end +&ifroom( 3i : blob_ex.ca ) + + Upon creation of the Jhash object, the &ital(root) fields, &cw(lodge_name,) &cw(member_count,) and &cw(grand_poobah) are immediately available. -The fields in the &ital(sub-blobs) are avalable only when the correct blob is selected. +The fields in the &ital(sub-blobs) are available only when the correct blob is selected. The code sample in figure &fig_blob_sample illustrates how a &ital(sub-blob) is selected. &ex_start jh->Set_blob( (char *) "address" ); // select address jh->Unset_blob(); // return to root - jh->Set_blob( (char *) "lease_info" ); // slect the lease blob + jh->Set_blob( (char *) "lease_info" ); // select the lease blob &ex_end -.gv fig -.sv _fig -&set_fref(fig_blob_sample:&_fig) -&fig(Blob selection example.) +.sv export_fig +&export_fig( fig_blob_sample ) +&fig_cen(Blob selection example.) &space Currently, the selected blob must be unset in order to select a blob at the root @@ -126,7 +128,7 @@ The following are the prototypes for the functions which allow values to be extr &ex_end &space -Each of these funcitons returns the value associated with the field with the given &ital(name.) +Each of these functions returns the value associated with the field with the given &ital(name.) If the value is missing, the following default values are returned: &half_space @@ -146,8 +148,8 @@ If the user needs to disambiguate between a missing value and the default value Two functions allow the developer to determine whether or not a field is included in the json. Both of these functions work on the current &ital(blob,) therefore it is important to ensure -that the correct blob is selected before using either of these funcitons. -The prototpyes for the &cw(Exists) and &cw(Missing) functions are below: +that the correct blob is selected before using either of these functions. +The prototypes for the &cw(Exists) and &cw(Missing) functions are below: &ex_start bool Exists( const char* name ); @@ -155,7 +157,7 @@ The prototpyes for the &cw(Exists) and &cw(Missing) functions are below: &ex_end The &cw(Exists) function returns &ital(true) if the field name exists in the json and &ital(false) otherwise. -Conversly, the &cw(Missing) funciton returns &ital(true) when the field name does not exist in the json. +Conversely, the &cw(Missing) function returns &ital(true) when the field name does not exist in the json. &h2(Testing Field Type) @@ -173,7 +175,7 @@ The following are the prototypes for these functions: &ex_end &space -Each of these funcitons return &ital(true) if the field with the given name is of the type +Each of these functions return &ital(true) if the field with the given name is of the type being tested for. @@ -202,7 +204,7 @@ For each of these functions the &cw(eidx) is the zero based element index which be tested or selected. &h3(Arrays of Blobs) -An array containing blobs, rather than simiple field value pairs, the blob must +An array containing blobs, rather than simple field value pairs, the blob must be selected prior to using it, just as a sub-blob needed to be selected. The &cw(Set_blob_ele) function is used to do this and has the following prototype: @@ -211,7 +213,7 @@ The &cw(Set_blob_ele) function is used to do this and has the following prototyp &ex_end &space -As with selecting a sub-blob, an unset must be preformed before selecting the next blob. +As with selecting a sub-blob, an unset must be performed before selecting the next blob. Figure &array_blob_code_fig illustrates how these functions can be used to read and print values from the json in figure &array_blob_json_fig. @@ -225,9 +227,8 @@ values from the json in figure &array_blob_json_fig. { "name": "Kyle Limestone", "member_num": 49 } ] &ex_end -.gv fig -&set_fref(array_blob_code_fig:&_fig) -&fig(Json array containing blobs.) +&export_fig(array_blob_code_fig) +&fig_cen(Json array containing blobs.) &space @@ -248,8 +249,7 @@ values from the json in figure &array_blob_json_fig. jh->Unset_blob(); // back to root } &ex_end -.gv fig -&set_fref(array_blob_json_fig:&_fig) -&fig(Code to process the array of blobs.) +&export_fig(array_blob_json_fig ) +&fig_cen(Code to process the array of blobs.) &space diff --git a/doc/src/user/raise_proto.im b/doc/src/user/raise_proto.im new file mode 100644 index 0000000..13f906f --- /dev/null +++ b/doc/src/user/raise_proto.im @@ -0,0 +1,57 @@ +.** vim: sw=4 ts=4 et : +.if false +================================================================================== + 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. +================================================================================== +.fi + + +.** we cant generate ncie tables it seems in any mark* language +.** so do nice formatting in pfm, and just embed the protos +.** for the bloody ascii based things. + +.ca start proto.ca +&ex_end + bool Raise( ); + bool Raise( int severity, int problem, std::string info ); + bool Raise( int severity, int problem, std::string info, + std::string addional_info ); + bool Raise_again( ); +&ex_end +.ca end + +.if pfm + + +&beg_table( 5.25i .3i : 80,15 ) +&col +.im proto.ca +&ex_start +(1) +(2) +(3) + +(4) +&ex_end +&end_table + + +.ei +.im proto.ca +.fi + +&fig_cen(The Raise function prototypes.) +&space diff --git a/doc/src/user/user_guide.xfm b/doc/src/user/user_guide.xfm index 4a469f7..e6e47f1 100644 --- a/doc/src/user/user_guide.xfm +++ b/doc/src/user/user_guide.xfm @@ -55,6 +55,7 @@ .** ------ major sections -------- .im cpp_frame.im .im jhash.im +.im alarm.im &h1(Example Programmes) The following sections contain several example programmes which are written on @@ -63,3 +64,4 @@ top of the C++ framework. .im example1.im .im example2.im .im example3.im +.im example4.im diff --git a/docs/rel-notes.rst b/docs/rel-notes.rst index 0be1017..935e799 100644 --- a/docs/rel-notes.rst +++ b/docs/rel-notes.rst @@ -1,3 +1,6 @@ +============= +RELEASE NOTES +============= .. This work is licensed under a Creative Commons Attribution 4.0 International License. .. SPDX-License-Identifier: CC-BY-4.0 .. @@ -6,28 +9,31 @@ .. Do NOT make changes directly to .rst or .md files. -============================================================================================ -Release Notes -============================================================================================ - C++ Framework Release Notes =========================== - The following is a list of release highlights for the C++ xAPP Framework. +Cherry Release +============== -2020 June 29; version 1.2.0 +2020 July 17; version 2.0.0 --------------------------- +Add alarm support. -Add support for json parsing +Add xapp namespace for Message, Msg_component, Alarm and Jhah +objects. This is a BREAKING change and thus the major version +number was bumped to 2. +2020 June 29; version 1.2.0 +--------------------------- +Add support for json parsing + 2020 June 26; version 1.1.0 --------------------------- - Change the port type in constructors to indicate "const" Version bump to 1.1.0 to allow patches to bronze code to @@ -38,19 +44,14 @@ continue to be done on the 1.0.* level. Bronze Release ============== - - 2020 April 28; version 1.0.0 ---------------------------- - Bump version to force package build (old CI version added incorrect install prefix). Bump to 1.0.0 for release. - 2020 March 27; version 0.1.2 ---------------------------- - Changes identified by sonar (missing copy/move builders) rmr_dump example programme Improvements to code for better test coverage diff --git a/docs/user-guide.rst b/docs/user-guide.rst index e0ac2e1..17f741d 100644 --- a/docs/user-guide.rst +++ b/docs/user-guide.rst @@ -36,6 +36,23 @@ involved to create an xApp instance, wait for a route table to arrive, send a message, and wait for messages to arrive. +The Namespace +------------- + +Starting with version 2.0.0 the framwork introduces a +*namespace* of ``xapp`` for the following classes and types: + + + * Alarm + * Jhash + * Message + * Msg_component + + +This is a breaking change and as such the major version was +bumpped from 1 to 2. + + Creating the xApp instance -------------------------- @@ -43,27 +60,27 @@ The creation of the xApp instance is as simple as invoking the object's constructor with two required parameters: - .. list-table:: - :widths: auto - :header-rows: 0 - :class: borderless + .. list-table:: + :widths: auto + :header-rows: 0 + :class: borderless - * - **port** - - - A C string (pointer to char) which defines the port that - RMR will open to listen for connections. + * - **port** + - + A C string (pointer to char) which defines the port that + RMR will open to listen for connections. - | + | - * - **wait** - - - A Boolean value which indicates whether or not the - initialization process should wait for the arrival of a - valid route table before completing. When true is - supplied, the initialization will not complete until RMR - has received a valid route table (or one is located via - the ``RMR_SEED_RT`` environment variable). + * - **wait** + - + A Boolean value which indicates whether or not the + initialization process should wait for the arrival of a + valid route table before completing. When true is + supplied, the initialization will not complete until RMR + has received a valid route table (or one is located via + the ``RMR_SEED_RT`` environment variable). @@ -73,16 +90,16 @@ creating the instance of the xApp object. :: - #include - #include - int main( ) { - std::unique_ptr xapp; - char* listen_port = (char *) "4560"; //RMR listen port - bool wait4table = true; // wait for a route table - - xapp = std::unique_ptr( - new Xapp( listen_port, wait4table ) ); - } + #include + #include + int main( ) { + std::unique_ptr xapp; + char* listen_port = (char *) "4560"; //RMR listen port + bool wait4table = true; // wait for a route table + + xapp = std::unique_ptr( + new Xapp( listen_port, wait4table ) ); + } Figure 1: Creating an xAPP instance. @@ -94,7 +111,7 @@ called ``man_ex1.cpp``. :: - g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread + g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread The above program, while complete and capable of being @@ -131,9 +148,9 @@ which callback functions must be defined with: :: - void cb_name( Message& m, int mtype, int subid, - int payload_len, Msg_component payload, - void* usr_data ); + void cb_name( xapp::Message& m, int mtype, int subid, + int payload_len, xapp::Msg_component payload, + void* usr_data ); Figure 2: Callback function signature @@ -141,55 +158,55 @@ The parameters passed to the callback function are as follows: - .. list-table:: - :widths: auto - :header-rows: 0 - :class: borderless + .. list-table:: + :widths: auto + :header-rows: 0 + :class: borderless - * - **m** - - - A reference to the Message that was received. + * - **m** + - + A reference to the Message that was received. - | + | - * - **mtype** - - - The message type (allows for disambiguation if the - callback is registered for multiple message types). + * - **mtype** + - + The message type (allows for disambiguation if the + callback is registered for multiple message types). - | + | - * - **subid** - - - The subscription ID from the message. + * - **subid** + - + The subscription ID from the message. - | + | - * - **payload len** - - - The number of bytes which the sender has placed into the - payload. + * - **payload len** + - + The number of bytes which the sender has placed into the + payload. - | + | - * - **payload** - - - A direct reference (smart pointer) to the payload. (The - smart pointer is wrapped in a special class in order to - provide a custom destruction function without burdening - the xApp developer with that knowledge.) + * - **payload** + - + A direct reference (smart pointer) to the payload. (The + smart pointer is wrapped in a special class in order to + provide a custom destruction function without burdening + the xApp developer with that knowledge.) - | + | - * - **user data** - - - A pointer to user data. This is the pointer that was - provided when the function was registered. + * - **user data** + - + A pointer to user data. This is the pointer that was + provided when the function was registered. @@ -201,35 +218,35 @@ section). :: - #include - #include - long m1000_count = 0; // message counters, one for each type - long m1001_count = 0; + #include + #include + long m1000_count = 0; // message counters, one for each type + long m1001_count = 0; - // callback function that will increase the appropriate counter - void cbf( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { - long* counter; + // callback function that will increase the appropriate counter + void cbf( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { + long* counter; - if( (counter = (long *) data) != NULL ) { - (*counter)++; - } - } + if( (counter = (long *) data) != NULL ) { + (*counter)++; + } + } - int main( ) { - std::unique_ptr xapp; - char* listen_port = (char *) "4560"; - bool wait4table = false; + int main( ) { + std::unique_ptr xapp; + char* listen_port = (char *) "4560"; + bool wait4table = false; - xapp = std::unique_ptr( - new Xapp( listen_port, wait4table ) ); + xapp = std::unique_ptr( + new Xapp( listen_port, wait4table ) ); - // register the same callback function for both msg types - xapp->Add_msg_cb( 1000, cbf, (void *) &m1000_count ); - xapp->Add_msg_cb( 1001, cbf, (void *) &m1001_count ); + // register the same callback function for both msg types + xapp->Add_msg_cb( 1000, cbf, (void *) &m1000_count ); + xapp->Add_msg_cb( 1001, cbf, (void *) &m1001_count ); - xapp->Run( 1 ); // start the callback driver - } + xapp->Run( 1 ); // start the callback driver + } Figure 3: Callback function example. @@ -283,10 +300,10 @@ message is a function of the message object itself and can take one of two forms: - * Replying to the sender of a received message + * Replying to the sender of a received message - * Sending a message (routed based on the message type and - subscription ID) + * Sending a message (routed based on the message type and + subscription ID) When replying to the sender, the message type and @@ -303,10 +320,10 @@ following prototypes. :: - bool Send_response( int mtype, int subid, int response_len, - std:shared_ptr response ); + bool Send_response( int mtype, int subid, int response_len, + std:shared_ptr response ); - bool Send_response( int response_len, std::shared_ptr response ); + bool Send_response( int response_len, std::shared_ptr response ); Figure 4: Reply function prototypes. @@ -324,8 +341,8 @@ message type, or the subscription ID, but not both, the :: - msg->Send_response( Message::NO_CHANGE, Message::NO_SUBID, - pl_length, (unsigned char *) payload ); + msg->Send_response( xapp::Message::NO_CHANGE, xapp::Message::NO_SUBID, + pl_length, (unsigned char *) payload ); Figure 5: Send response prototype. @@ -343,16 +360,16 @@ are shown below. :: - bool Send_msg( int mtype, int subid, int payload_len, - std::shared_ptr payload ); + bool Send_msg( int mtype, int subid, int payload_len, + std::shared_ptr payload ); - bool Send_msg( int mtype, int subid, int payload_len, - unsigned char* payload ); + bool Send_msg( int mtype, int subid, int payload_len, + unsigned char* payload ); - bool Send_msg( int payload_len, - std::shared_ptr payload ); + bool Send_msg( int payload_len, + std::shared_ptr payload ); - bool Send_msg( int payload_len, unsigned char* payload ); + bool Send_msg( int payload_len, unsigned char* payload ); Figure 6: Send function prototypes. @@ -397,15 +414,15 @@ not sent). :: - Msg_component payload; // smart reference - int pl_size; // max size of payload + Msg_component payload; // smart reference + int pl_size; // max size of payload - payload = msg->Get_payload(); - pl_size = msg->Get_available_size(); - if( snprintf( (char *) payload.get(), pl_size, - "Msg Received\\n" ) < pl_size ) { - msg->Send_response( M_TYPE, SID, strlen( raw_pl ), NULL ); - } + payload = msg->Get_payload(); + pl_size = msg->Get_available_size(); + if( snprintf( (char *) payload.get(), pl_size, + "Msg Received\\n" ) < pl_size ) { + msg->Send_response( M_TYPE, SID, strlen( raw_pl ), NULL ); + } Figure 7: Send message without buffer copy. @@ -474,12 +491,12 @@ to the constructor. :: - #include + #include - std::string jstring = "{ \\"tag\\": \\"Hello World\\" }"; - Jhash* jh; + std::string jstring = "{ \\"tag\\": \\"Hello World\\" }"; + Jhash* jh; - jh = new Jhash( jstring.c_str() ); + jh = new Jhash( jstring.c_str() ); Figure 8: The creation of the Jhash object. @@ -496,44 +513,44 @@ this representation. The approach taken by Jhash is a "directory view" approach, where the "current directory," or current *blob,* limits the scope of visible fields. -As an example, the json contained in figure jblob_fig, -contains a "root" blob and two *sub-blobs* (address and -lease_info). +As an example, the json contained in figure 9, contains a +"root" blob and two *sub-blobs* (address and lease_info). -:: - { - "lodge_name": "Water Buffalo Lodge 714", - "member_count": 41, - "grand_poobah": "Larry K. Slate", - "attendance": [ 23, 14, 41, 38, 24 ], - "address": { - "street": "16801 Stonway Lane", - "suite": null, - "city": "Bedrock", - "post_code": "45701" - }, - "lease_info": { - "owner": "Stonegate Properties", - "amount": 216.49, - "due": "monthly", - "contact:" "Kyle Limestone" - } - } +:: -Figure 9: Sample json with a root and too blobs. + { + "lodge_name": "Water Buffalo Lodge 714", + "member_count": 41, + "grand_poobah": "Larry K. Slate", + "attendance": [ 23, 14, 41, 38, 24 ], + "address": { + "street": "16801 Stonway Lane", + "suite": null, + "city": "Bedrock", + "post_code": "45701" + }, + "lease_info": { + "owner": "Stonegate Properties", + "amount": 216.49, + "due": "monthly", + "contact:" "Kyle Limestone" + } + } + +Figure 9: Sample json with a root and two blobs. Upon creation of the Jhash object, the *root* fields, ``lodge_name,`` ``member_count,`` and ``grand_poobah`` are immediately available. The fields in the *sub-blobs* are -avalable only when the correct blob is selected. The code +available only when the correct blob is selected. The code sample in figure 10 illustrates how a *sub-blob* is selected. :: - jh->Set_blob( (char *) "address" ); // select address - jh->Unset_blob(); // return to root - jh->Set_blob( (char *) "lease_info" ); // slect the lease blob + jh->Set_blob( (char *) "address" ); // select address + jh->Unset_blob(); // return to root + jh->Set_blob( (char *) "lease_info" ); // select the lease blob Figure 10: Blob selection example. @@ -560,36 +577,36 @@ extracted: :: - std::string String( const char* name ); - float Value( const char* name ); - bool Bool( const char* name ); + std::string String( const char* name ); + float Value( const char* name ); + bool Bool( const char* name ); -Each of these funcitons returns the value associated with the +Each of these functions returns the value associated with the field with the given *name.* If the value is missing, the following default values are returned: - .. list-table:: - :widths: 15,80 - :header-rows: 0 - :class: borderless + .. list-table:: + :widths: 15,80 + :header-rows: 0 + :class: borderless - * - **String** - - - An empty string (.e.g ""). + * - **String** + - + An empty string (.e.g ""). - | + | - * - **Value** - - - Zero (e.g 0.0) + * - **Value** + - + Zero (e.g 0.0) - | + | - * - **bool** - - - false + * - **bool** + - + false @@ -605,17 +622,17 @@ Two functions allow the developer to determine whether or not a field is included in the json. Both of these functions work on the current *blob,* therefore it is important to ensure that the correct blob is selected before using either of -these funcitons. The prototpyes for the ``Exists`` and +these functions. The prototypes for the ``Exists`` and ``Missing`` functions are below: :: - bool Exists( const char* name ); - bool Is_missing( const char* name ); + bool Exists( const char* name ); + bool Is_missing( const char* name ); The ``Exists`` function returns *true* if the field name -exists in the json and *false* otherwise. Conversly, the -``Missing`` funciton returns *true* when the field name does +exists in the json and *false* otherwise. Conversely, the +``Missing`` function returns *true* when the field name does not exist in the json. @@ -630,13 +647,13 @@ for these functions: :: - bool Is_bool( const char* name ); - bool Is_null( const char* name ); - bool Is_string( const char* name ); - bool Is_value( const char* name ); + bool Is_bool( const char* name ); + bool Is_null( const char* name ); + bool Is_string( const char* name ); + bool Is_value( const char* name ); -Each of these funcitons return *true* if the field with the +Each of these functions return *true* if the field with the given name is of the type being tested for. @@ -652,16 +669,16 @@ based functions are below: :: - int Array_len( const char* name ); + int Array_len( const char* name ); - bool Is_bool_ele( const char* name, int eidx ); - bool Is_null_ele( const char* name, int eidx ); - bool Is_string_ele( const char* name, int eidx ); - bool Is_value_ele( const char* name, int eidx ); + bool Is_bool_ele( const char* name, int eidx ); + bool Is_null_ele( const char* name, int eidx ); + bool Is_string_ele( const char* name, int eidx ); + bool Is_value_ele( const char* name, int eidx ); - bool Bool_ele( const char* name, int eidx ); - std::string String_ele( const char* name, int eidx ); - float Value_ele( const char* name, int eidx ); + bool Bool_ele( const char* name, int eidx ); + std::string String_ele( const char* name, int eidx ); + float Value_ele( const char* name, int eidx ); For each of these functions the ``eidx`` is the zero based @@ -671,54 +688,249 @@ element index which is to be tested or selected. Arrays of Blobs --------------- -An array containing blobs, rather than simiple field value +An array containing blobs, rather than simple field value pairs, the blob must be selected prior to using it, just as a sub-blob needed to be selected. The ``Set_blob_ele`` function is used to do this and has the following prototype: :: - bool Set_blob_ele( const char* name, int eidx ); + bool Set_blob_ele( const char* name, int eidx ); -As with selecting a sub-blob, an unset must be preformed +As with selecting a sub-blob, an unset must be performed before selecting the next blob. Figure 11 illustrates how these functions can be used to read and print values from the json in figure 12. :: - "members": [ - { "name": "Fred Flinstone", "member_num": 42 }, - { "name": "Barney Rubble", "member_num": 48 }, - { "name": "Larry K Slate", "member_num": 22 }, - { "name": "Kyle Limestone", "member_num": 49 } - ] + "members": [ + { "name": "Fred Flinstone", "member_num": 42 }, + { "name": "Barney Rubble", "member_num": 48 }, + { "name": "Larry K Slate", "member_num": 22 }, + { "name": "Kyle Limestone", "member_num": 49 } + ] Figure 11: Json array containing blobs. :: - std::string mname; - float mnum; - int len; + std::string mname; + float mnum; + int len; - len = jh->Array_len( (char *) "members" ); - for( i = 0; i < len; i++ ) { - jh->Set_blob_ele( (char *) "members", i ); // select blob + len = jh->Array_len( (char *) "members" ); + for( i = 0; i < len; i++ ) { + jh->Set_blob_ele( (char *) "members", i ); // select blob - mname = jh->String( (char *) "name" ); // read values - mnum = jh->Value( (char *) "member_num" ); - fprintf( stdout, "%s is member %d\\n", mname.c_str(), (int) mnum ); + mname = jh->String( (char *) "name" ); // read values + mnum = jh->Value( (char *) "member_num" ); + fprintf( stdout, "%s is member %d\\n", mname.c_str(), (int) mnum ); - jh->Unset_blob(); // back to root - } + jh->Unset_blob(); // back to root + } Figure 12: Code to process the array of blobs. +ALARM MANAGER INTERFACE +======================= + +The C++ framework provides an API which allows the xAPP to +easily construct and generate alarm messages. Alarm messages +are a special class of RMR message, allocated in a similar +fashion as an RMR message through the framework's +``Alloc_alarm()`` function. + +The API consists of the following function types: + + + .. list-table:: + :widths: auto + :header-rows: 0 + :class: borderless + + * - **Raise** + - + Cause the alarm to be assigned a severity and and sent via + RMR message to the alarm collector process. + + + | + + * - **Clear** + - + Cause a clear message to be sent to the alarm collector. + + + | + + * - **Raise Again** + - + Cause a clear followed by a raise message to be sent to + the alarm collector. + + + + + +Allocating Alarms +----------------- + +The ``xapp`` function provided by the framework is used to +create an alarm object. Once the xAPP has an alarm object it +can be used to send one, or more, alarm messages to the +collector. + +The allocation function has three prototypes which allow the +xAPP to create an alarm with an initial set of information as +is appropriate. The following are the prototypes for the +allocate functions: + + +:: + + std::unique_ptr Alloc_alarm( ); + std::unique_ptr Alloc_alarm( std::string meid ); + std::unique_ptr Alloc_alarm( int prob_id, std::string meid ); + +Figure 13: Alarm allocation prototypes. + +Each of the allocation functions returns a unique pointer to +the alarm. In the simplest form (1) the alarm is initialised +with an empty meid (managed element ID) string, and unset +problem ID (-1). The second prototype allows the xAPP to +supply the meid, and in the third form both the problem ID +and the meid are used to initialise the alarm. + + +Raising An Alarm +---------------- + +Once an alarm has been allocated, its ``Raise()`` function +can be used to cause the alarm to be sent to the collector. +The raise process allows the xAPP to perform the following +modifications to the alarm before sending the message: + + + * Set the alarm severity + + * Set the problem ID value + + * Set the alarm information string + + * Set the additional information string + + +The following are the prototypes for the ``Raise()`` +functions of an alarm object: ..... In its simplest form (1) +the ``Raise()`` function will send the alarm without making +any changes to the data. The final two forms allow the xAPP +to supply additional data which is added to the alarm before +sending the message. Each of the raise functions returns +``true`` on success and ``false`` if the alarm message could +not be sent. + + +Severity +-------- + +The severity is one of the ``SEV_`` constants listed below. +These map to alarm collector strings and insulate the xAPP +from any future alarm collector changes. The specific meaning +of these severity types are defined by the alarm collector +and thus no attempt is made to guess what their actual +meaning is. These constants are available by including +``alarm.hpp.`` + + + :: + + SEV_MAJOR + SEV_MINOR + SEV_WARN + SEV_DEFAULT + +Figure 14: Severity constants available in alarm.hpp. + + +The Problem ID +-------------- + +The problem ID is an integer which is assigned by the xAPP. +The framework makes no attempt to verify that it has been se, +nor does it attempt to validate the value. If the xAPP does +not set the value, ``-1`` is used. + + +Information Strings +------------------- + +The two information strings are also xAPP defined and provide +the information that the xAPP deems necessary and related to +the alarm. What the collector expects, and how these strings +are used, is beyond the scope of the framework to describe or +validate. If not supplied, empty strings are sent in the +alarm message. + + +Clearing An Alarm +----------------- + +The ``Clear()`` function of an alarm may be used to send a +clear message. In a manner similar to the ``Raise()`` +functions, the ``Clear()`` functions allow the existing alarm +data to be sent without change, or for the xAPP to modify the +data before the message is sent to the collector. The +following are the prototype for these functions. + +:: + + bool Clear( ); + bool Clear( int severity, int problem, std::string info ); + bool Clear( int severity, int problem, std::string info, std::string addional_info ); + bool Clear_all( ); + + +Figure 15: Clear function prototypes. + +Each of the clear functions returns ``true`` on success and +``false`` if the alarm message could not be sent. + +The ``Clear_all()`` function sends a special action code to +the collector which is assumed to clear all alarms. However, +it is unknown whether that implies **all** alarms, or all +alarms matching the ``problem_id,`` or some other +interpretation. Please consult the alarm collector +documentation for these specifics. + + +Adjusting Alarm Contents +------------------------ + +It might be necessary for the xAPP to adjust the alarm +contents outside of the scope of the ``Raise()`` function, or +to adjust data that cannot be manipulated by ``Raise().`` The +following are the (self explanatory) prototypes for the +*setter* functions which are available to the xAPP. + + +:: + + void Set_additional( std::string new_info ); + void Set_appid( std::string new_id ); + void Set_info( std::string new_info ); + void Set_meid( std::string new_meid ); + void Set_problem( int new_id ); + void Set_severity( int new_sev ); + +Figure 16: Alarm Setters + + + EXAMPLE PROGRAMMES ================== @@ -762,233 +974,233 @@ omitted. The full code is in the framework repository. - :: - - #include - #include - #include - - #include "ricxfcpp/xapp.hpp" - - /* - Information that the callback needs outside - of what is given to it via parms on a call - by the framework. - */ - typedef struct { - int vlevel; // verbosity level - bool forward; // if true, message is forwarded - int stats_freq; // header/stats after n messages - std::atomic pcount; // messages processed - std::atomic icount; // messages ignored - std::atomic hdr; // number of messages before next header - } cb_info_t; - - // ---------------------------------------------------------------------- - - /* - Dump bytes to tty. - */ - void dump( unsigned const char* buf, int len ) { - int i; - int j; - char cheater[17]; - - fprintf( stdout, " 0000 | " ); - j = 0; - for( i = 0; i < len; i++ ) { - cheater[j++] = isprint( buf[i] ) ? buf[i] : '.'; - fprintf( stdout, "%02x ", buf[i] ); - - if( j == 16 ) { - cheater[j] = 0; - fprintf( stdout, " | %s\\n %04x | ", cheater, i+1 ); - j = 0; - } - } - - if( j ) { - i = 16 - (i % 16); - for( ; i > 0; i-- ) { - fprintf( stdout, " " ); - } - cheater[j] = 0; - fprintf( stdout, " | %s\\n", cheater ); - } - } - - /* - generate stats when the hdr count reaches 0. Only one active - thread will ever see it be exactly 0, so this is thread safe. - */ - void stats( cb_info_t& cbi ) { - int curv; // current stat trigger value - - curv = cbi.hdr--; - - if( curv == 0 ) { // stats when we reach 0 - fprintf( stdout, "ignored: %ld processed: %ld\\n", - cbi.icount.load(), cbi.pcount.load() ); - if( cbi.vlevel > 0 ) { - fprintf( stdout, "\\n %5s %5s %2s %5s\\n", - "MTYPE", "SUBID", "ST", "PLLEN" ); - } + :: + + #include + #include + #include + + #include "ricxfcpp/xapp.hpp" + + /* + Information that the callback needs outside + of what is given to it via parms on a call + by the framework. + */ + typedef struct { + int vlevel; // verbosity level + bool forward; // if true, message is forwarded + int stats_freq; // header/stats after n messages + std::atomic pcount; // messages processed + std::atomic icount; // messages ignored + std::atomic hdr; // number of messages before next header + } cb_info_t; + + // ---------------------------------------------------------------------- + + /* + Dump bytes to tty. + */ + void dump( unsigned const char* buf, int len ) { + int i; + int j; + char cheater[17]; + + fprintf( stdout, " 0000 | " ); + j = 0; + for( i = 0; i < len; i++ ) { + cheater[j++] = isprint( buf[i] ) ? buf[i] : '.'; + fprintf( stdout, "%02x ", buf[i] ); + + if( j == 16 ) { + cheater[j] = 0; + fprintf( stdout, " | %s\\n %04x | ", cheater, i+1 ); + j = 0; + } + } - cbi.hdr = cbi.stats_freq; // reset must be last - } - } + if( j ) { + i = 16 - (i % 16); + for( ; i > 0; i-- ) { + fprintf( stdout, " " ); + } + cheater[j] = 0; + fprintf( stdout, " | %s\\n", cheater ); + } + } - void cb1( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { - cb_info_t* cbi; - long total_count; + /* + generate stats when the hdr count reaches 0. Only one active + thread will ever see it be exactly 0, so this is thread safe. + */ + void stats( cb_info_t& cbi ) { + int curv; // current stat trigger value - if( (cbi = (cb_info_t *) data) == NULL ) { - return; - } + curv = cbi.hdr--; - cbi->pcount++; - stats( *cbi ); // gen stats & header if needed + if( curv == 0 ) { // stats when we reach 0 + fprintf( stdout, "ignored: %ld processed: %ld\\n", + cbi.icount.load(), cbi.pcount.load() ); + if( cbi.vlevel > 0 ) { + fprintf( stdout, "\\n %5s %5s %2s %5s\\n", + "MTYPE", "SUBID", "ST", "PLLEN" ); + } - if( cbi->vlevel > 0 ) { - fprintf( stdout, " %-5d %-5d %02d %-5d \\n", - mtype, subid, mbuf.Get_state(), len ); + cbi.hdr = cbi.stats_freq; // reset must be last + } + } - if( cbi->vlevel > 1 ) { - dump( payload.get(), len > 64 ? 64 : len ); - } - } + void cb1( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { + cb_info_t* cbi; + long total_count; - if( cbi->forward ) { - // forward with no change to len or payload - mbuf.Send_msg( Message::NO_CHANGE, NULL ); - } - } - - /* - registered as the default callback; it counts the - messages that we aren't giving details about. - */ - void cbd( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { - cb_info_t* cbi; - - if( (cbi = (cb_info_t *) data) == NULL ) { - return; - } - - cbi->icount++; - stats( *cbi ); + if( (cbi = (cb_info_t *) data) == NULL ) { + return; + } - if( cbi->forward ) { - // forward with no change to len or payload - mbuf.Send_msg( Message::NO_CHANGE, NULL ); - } - } - - int main( int argc, char** argv ) { - std::unique_ptr x; - char* port = (char *) "4560"; - int ai = 1; // arg processing index - cb_info_t* cbi; - int ncb = 0; // number of callbacks registered - int mtype; - int nthreads = 1; - - cbi = (cb_info_t *) malloc( sizeof( *cbi ) ); - cbi->pcount = 0; - cbi->icount = 0; - cbi->stats_freq = 10; - - ai = 1; - // very simple flag parsing (no error/bounds checking) - while( ai < argc ) { - if( argv[ai][0] != '-' ) { // break on first non-flag - break; - } + cbi->pcount++; + stats( *cbi ); // gen stats & header if needed - // very simple arg parsing; each must be separate -x -y not -xy. - switch( argv[ai][1] ) { - case 'f': // enable packet forwarding - cbi->forward = true; - break; + if( cbi->vlevel > 0 ) { + fprintf( stdout, " %-5d %-5d %02d %-5d \\n", + mtype, subid, mbuf.Get_state(), len ); - case 'p': // define port - port = argv[ai+1]; - ai++; - break; + if( cbi->vlevel > 1 ) { + dump( payload.get(), len > 64 ? 64 : len ); + } + } - case 's': // stats frequency - cbi->stats_freq = atoi( argv[ai+1] ); - if( cbi->stats_freq < 5 ) { // enforce sanity - cbi->stats_freq = 5; - } - ai++; - break; + if( cbi->forward ) { + // forward with no change to len or payload + mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL ); + } + } - case 't': // thread count - nthreads = atoi( argv[ai+1] ); - if( nthreads < 1 ) { - nthreads = 1; - } - ai++; - break; + /* + registered as the default callback; it counts the + messages that we aren't giving details about. + */ + void cbd( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { + cb_info_t* cbi; - case 'v': // simple verbose bump - cbi->vlevel++; - break; + if( (cbi = (cb_info_t *) data) == NULL ) { + return; + } - case 'V': // explicit verbose level - cbi->vlevel = atoi( argv[ai+1] ); - ai++; - break; + cbi->icount++; + stats( *cbi ); - default: - fprintf( stderr, "unrecognised option: %s\\n", argv[ai] ); - fprintf( stderr, "usage: %s [-f] [-p port] " - "[-s stats-freq] [-t thread-count] " - "[-v | -V n] msg-type1 ... msg-typen\\n", - argv[0] ); - fprintf( stderr, "\\tstats frequency is based on # of messages received\\n" ); - fprintf( stderr, "\\tverbose levels (-V) 0 counts only, " - "1 message info 2 payload dump\\n" ); - exit( 1 ); - } + if( cbi->forward ) { + // forward with no change to len or payload + mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL ); + } + } - ai++; - } + int main( int argc, char** argv ) { + std::unique_ptr x; + char* port = (char *) "4560"; + int ai = 1; // arg processing index + cb_info_t* cbi; + int ncb = 0; // number of callbacks registered + int mtype; + int nthreads = 1; + + cbi = (cb_info_t *) malloc( sizeof( *cbi ) ); + cbi->pcount = 0; + cbi->icount = 0; + cbi->stats_freq = 10; + + ai = 1; + // very simple flag parsing (no error/bounds checking) + while( ai < argc ) { + if( argv[ai][0] != '-' ) { // break on first non-flag + break; + } + + // very simple arg parsing; each must be separate -x -y not -xy. + switch( argv[ai][1] ) { + case 'f': // enable packet forwarding + cbi->forward = true; + break; + + case 'p': // define port + port = argv[ai+1]; + ai++; + break; + + case 's': // stats frequency + cbi->stats_freq = atoi( argv[ai+1] ); + if( cbi->stats_freq < 5 ) { // enforce sanity + cbi->stats_freq = 5; + } + ai++; + break; + + case 't': // thread count + nthreads = atoi( argv[ai+1] ); + if( nthreads < 1 ) { + nthreads = 1; + } + ai++; + break; + + case 'v': // simple verbose bump + cbi->vlevel++; + break; + + case 'V': // explicit verbose level + cbi->vlevel = atoi( argv[ai+1] ); + ai++; + break; + + default: + fprintf( stderr, "unrecognised option: %s\\n", argv[ai] ); + fprintf( stderr, "usage: %s [-f] [-p port] " + "[-s stats-freq] [-t thread-count] " + "[-v | -V n] msg-type1 ... msg-typen\\n", + argv[0] ); + fprintf( stderr, "\\tstats frequency is based on # of messages received\\n" ); + fprintf( stderr, "\\tverbose levels (-V) 0 counts only, " + "1 message info 2 payload dump\\n" ); + exit( 1 ); + } + + ai++; + } - cbi->hdr = cbi->stats_freq; - fprintf( stderr, " listening on port: %s\\n", port ); + cbi->hdr = cbi->stats_freq; + fprintf( stderr, " listening on port: %s\\n", port ); - // create xapp, wait for route table if forwarding - x = std::unique_ptr( new Xapp( port, cbi->forward ) ); + // create xapp, wait for route table if forwarding + x = std::unique_ptr( new Xapp( port, cbi->forward ) ); - // register callback for each type on the command line - while( ai < argc ) { - mtype = atoi( argv[ai] ); - ai++; - fprintf( stderr, " capturing messages for type %d\\n", mtype ); - x->Add_msg_cb( mtype, cb1, cbi ); - ncb++; - } + // register callback for each type on the command line + while( ai < argc ) { + mtype = atoi( argv[ai] ); + ai++; + fprintf( stderr, " capturing messages for type %d\\n", mtype ); + x->Add_msg_cb( mtype, cb1, cbi ); + ncb++; + } - if( ncb < 1 ) { - fprintf( stderr, " no message types specified on the command line\\n" ); - exit( 1 ); - } + if( ncb < 1 ) { + fprintf( stderr, " no message types specified on the command line\\n" ); + exit( 1 ); + } - x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi ); // register default cb + x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi ); // register default cb - fprintf( stderr, " starting driver\\n" ); - x->Run( nthreads ); + fprintf( stderr, " starting driver\\n" ); + x->Run( nthreads ); - // return from run() is not expected, but some compilers might - // compilain if there isn't a return value here. - return 0; - } + // return from run() is not expected, but some compilers might + // compilain if there isn't a return value here. + return 0; + } - Figure 13: Simple callback application. + Figure 17: Simple callback application. Callback Receiver @@ -1010,87 +1222,87 @@ this example as simple as possible, the counters are not locked when incremented. - :: + :: - #include + #include - #include "ricxfcpp/message.hpp" - #include "ricxfcpp/msg_component.hpp" - #include "ricxfcpp/xapp.hpp" + #include "ricxfcpp/message.hpp" + #include "ricxfcpp/msg_component.hpp" + #include "ricxfcpp/xapp.hpp" - // counts; not thread safe - long cb1_count = 0; - long cb2_count = 0; - long cbd_count = 0; + // counts; not thread safe + long cb1_count = 0; + long cb2_count = 0; + long cbd_count = 0; - long cb1_lastts = 0; - long cb1_lastc = 0; + long cb1_lastts = 0; + long cb1_lastc = 0; - // respond with 2 messages for each type 1 received - void cb1( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { - long now; - long total_count; + // respond with 2 messages for each type 1 received + void cb1( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { + long now; + long total_count; - // illustrate that we can use the same buffer for 2 rts calls - mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK1\\n" ); - mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\\n" ); + // illustrate that we can use the same buffer for 2 rts calls + mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK1\\n" ); + mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\\n" ); - cb1_count++; - } - - // just count messages - void cb2( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { - cb2_count++; - } - - // default to count all unrecognised messages - void cbd( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { - cbd_count++; - } - - int main( int argc, char** argv ) { - Xapp* x; - char* port = (char *) "4560"; - int ai = 1; // arg processing index - int nthreads = 1; - - // very simple flag processing (no bounds/error checking) - while( ai < argc ) { - if( argv[ai][0] != '-' ) { - break; - } + cb1_count++; + } - switch( argv[ai][1] ) { // we only support -x so -xy must be -x -y - case 'p': - port = argv[ai+1]; - ai++; - break; + // just count messages + void cb2( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { + cb2_count++; + } - case 't': - nthreads = atoi( argv[ai+1] ); - ai++; - break; - } + // default to count all unrecognised messages + void cbd( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { + cbd_count++; + } - ai++; - } + int main( int argc, char** argv ) { + Xapp* x; + char* port = (char *) "4560"; + int ai = 1; // arg processing index + int nthreads = 1; + + // very simple flag processing (no bounds/error checking) + while( ai < argc ) { + if( argv[ai][0] != '-' ) { + break; + } + + switch( argv[ai][1] ) { // we only support -x so -xy must be -x -y + case 'p': + port = argv[ai+1]; + ai++; + break; + + case 't': + nthreads = atoi( argv[ai+1] ); + ai++; + break; + } + + ai++; + } - fprintf( stderr, " listening on port: %s\\n", port ); - fprintf( stderr, " starting %d threads\\n", nthreads ); + fprintf( stderr, " listening on port: %s\\n", port ); + fprintf( stderr, " starting %d threads\\n", nthreads ); - x = new Xapp( port, true ); - x->Add_msg_cb( 1, cb1, NULL ); // register callbacks - x->Add_msg_cb( 2, cb2, NULL ); - x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL ); + x = new Xapp( port, true ); + x->Add_msg_cb( 1, cb1, NULL ); // register callbacks + x->Add_msg_cb( 2, cb2, NULL ); + x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL ); - x->Run( nthreads ); // let framework drive - // control should not return - } + x->Run( nthreads ); // let framework drive + // control should not return + } - Figure 14: Simple callback application. + Figure 18: Simple callback application. @@ -1104,103 +1316,229 @@ checking is skipped, and short cuts have been made in order to keep the example small and to the point. - :: + :: + + #include + #include + #include - #include - #include - #include + #include + #include - #include - #include + #include "ricxfcpp/xapp.hpp" + + extern int main( int argc, char** argv ) { + std::unique_ptr xfw; + std::unique_ptr msg; + xapp::Msg_component payload; // special type of unique pointer to the payload + + int sz; + int len; + int i; + int ai; + int response_to = 0; // max timeout wating for a response + char* port = (char *) "4555"; + int mtype = 0; + int rmtype; // received message type + int delay = 1000000; // mu-sec delay; default 1s + + + // very simple flag processing (no bounds/error checking) + while( ai < argc ) { + if( argv[ai][0] != '-' ) { + break; + } + + // we only support -x so -xy must be -x -y + switch( argv[ai][1] ) { + // delay between messages (mu-sec) + case 'd': + delay = atoi( argv[ai+1] ); + ai++; + break; + + case 'p': + port = argv[ai+1]; + ai++; + break; + + // timeout in seconds; we need to convert to ms for rmr calls + case 't': + response_to = atoi( argv[ai+1] ) * 1000; + ai++; + break; + } + ai++; + } - #include "ricxfcpp/xapp.hpp" + fprintf( stderr, " response timeout set to: %d\\n", response_to ); + fprintf( stderr, " listening on port: %s\\n", port ); + + // get an instance and wait for a route table to be loaded + xfw = std::unique_ptr( new Xapp( port, true ) ); + msg = xfw->Alloc_msg( 2048 ); + + for( i = 0; i < 100; i++ ) { + mtype++; + if( mtype > 10 ) { + mtype = 0; + } + + // we'll reuse a received message; get max size + sz = msg->Get_available_size(); + + // direct access to payload; add something silly + payload = msg->Get_payload(); + len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i ); + + // payload updated in place, prevent copy by passing nil + if ( ! msg->Send_msg( mtype, xapp::Message::NO_SUBID, len, NULL )) { + fprintf( stderr, " send failed: %d\\n", i ); + } + + // receive anything that might come back + msg = xfw->Receive( response_to ); + if( msg != NULL ) { + rmtype = msg->Get_mtype(); + payload = msg->Get_payload(); + fprintf( stderr, "got: mtype=%d payload=(%s)\\n", + rmtype, (char *) payload.get() ); + } else { + msg = xfw->Alloc_msg( 2048 ); + } + + if( delay > 0 ) { + usleep( delay ); + } + } + } - extern int main( int argc, char** argv ) { - std::unique_ptr xfw; - std::unique_ptr msg; - Msg_component payload; // special type of unique pointer to the payload + Figure 19: Simple looping sender application. - int sz; - int len; - int i; - int ai; - int response_to = 0; // max timeout wating for a response - char* port = (char *) "4555"; - int mtype = 0; - int rmtype; // received message type - int delay = 1000000; // mu-sec delay; default 1s - // very simple flag processing (no bounds/error checking) - while( ai < argc ) { - if( argv[ai][0] != '-' ) { - break; - } +Alarm Example +------------- - // we only support -x so -xy must be -x -y - switch( argv[ai][1] ) { - // delay between messages (mu-sec) - case 'd': - delay = atoi( argv[ai+1] ); - ai++; - break; + This is an extension of a previous example which sends an + alarm during initialisation and clears the alarm as soon + as messages are being received. It is unknown if this is + the type of alarm that is expected at the collector, but + illustrates how an alarm is allocated, raised and cleared. - case 'p': - port = argv[ai+1]; - ai++; - break; - // timeout in seconds; we need to convert to ms for rmr calls - case 't': - response_to = atoi( argv[ai+1] ) * 1000; - ai++; + :: + + + #include + #include + #include + + #include + #include + + #include "ricxfcpp/xapp.hpp" + #include "ricxfcpp/alarm.hpp" + + extern int main( int argc, char** argv ) { + std::unique_ptr xfw; + std::unique_ptr msg; + xapp::Msg_component payload; // special type of unique pointer to the payload + std::unique_ptr alarm; + + bool received = false; // false until we've received a message + int sz; + int len; + int i; + int ai = 1; + int response_to = 0; // max timeout wating for a response + char* port = (char *) "4555"; + int mtype = 0; + int rmtype; // received message type + int delay = 1000000; // mu-sec delay; default 1s + + + // very simple flag processing (no bounds/error checking) + while( ai < argc ) { + if( argv[ai][0] != '-' ) { break; + } + + // we only support -x so -xy must be -x -y + switch( argv[ai][1] ) { + // delay between messages (mu-sec) + case 'd': + delay = atoi( argv[ai+1] ); + ai++; + break; + + case 'p': + port = argv[ai+1]; + ai++; + break; + + // timeout in seconds; we need to convert to ms for rmr calls + case 't': + response_to = atoi( argv[ai+1] ) * 1000; + ai++; + break; + } + ai++; } - ai++; - } - fprintf( stderr, " response timeout set to: %d\\n", response_to ); - fprintf( stderr, " listening on port: %s\\n", port ); + fprintf( stderr, " response timeout set to: %d\\n", response_to ); + fprintf( stderr, " listening on port: %s\\n", port ); - // get an instance and wait for a route table to be loaded - xfw = std::unique_ptr( new Xapp( port, true ) ); - msg = xfw->Alloc_msg( 2048 ); + // get an instance and wait for a route table to be loaded + xfw = std::unique_ptr( new Xapp( port, true ) ); + msg = xfw->Alloc_msg( 2048 ); - for( i = 0; i < 100; i++ ) { - mtype++; - if( mtype > 10 ) { - mtype = 0; - } - // we'll reuse a received message; get max size - sz = msg->Get_available_size(); + // raise an unavilable alarm which we'll clear on the first recevied message + alarm = xfw->Alloc_alarm( "meid-1234" ); + alarm->Raise( xapp::Alarm::SEV_MINOR, 13, "unavailable", "no data recevied" ); - // direct access to payload; add something silly - payload = msg->Get_payload(); - len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i ); + for( i = 0; i < 100; i++ ) { + mtype++; + if( mtype > 10 ) { + mtype = 0; + } - // payload updated in place, prevent copy by passing nil - if ( ! msg->Send_msg( mtype, Message::NO_SUBID, len, NULL )) { - fprintf( stderr, " send failed: %d\\n", i ); - } + // we'll reuse a received message; get max size + sz = msg->Get_available_size(); - // receive anything that might come back - msg = xfw->Receive( response_to ); - if( msg != NULL ) { - rmtype = msg->Get_mtype(); + // direct access to payload; add something silly payload = msg->Get_payload(); - fprintf( stderr, "got: mtype=%d payload=(%s)\\n", - rmtype, (char *) payload.get() ); - } else { - msg = xfw->Alloc_msg( 2048 ); - } + len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i ); + + // payload updated in place, prevent copy by passing nil + if ( ! msg->Send_msg( mtype, xapp::Message::NO_SUBID, len, NULL )) { + fprintf( stderr, " send failed: %d\\n", i ); + } + + // receive anything that might come back + msg = xfw->Receive( response_to ); + if( msg != NULL ) { + if( ! received ) { + alarm->Clear( xapp::Alarm::SEV_MINOR, 13, "messages flowing", "" ); // clear the alarm on first received message + received = true; + } - if( delay > 0 ) { - usleep( delay ); + rmtype = msg->Get_mtype(); + payload = msg->Get_payload(); + fprintf( stderr, "got: mtype=%d payload=(%s)\\n", + rmtype, (char *) payload.get() ); + } else { + msg = xfw->Alloc_msg( 2048 ); + } + + if( delay > 0 ) { + usleep( delay ); + } } } - } - Figure 15: Simple looping sender application. + Figure 20: Simple looping sender application with alarm + generation. diff --git a/examples/rmr_dump.cpp b/examples/rmr_dump.cpp index 52fe23d..860caf5 100644 --- a/examples/rmr_dump.cpp +++ b/examples/rmr_dump.cpp @@ -117,8 +117,8 @@ void stats( cb_info_t& cbi ) { } } -void cb1( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { +void cb1( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { cb_info_t* cbi; long total_count; @@ -140,7 +140,7 @@ void cb1( Message& mbuf, int mtype, int subid, int len, if( cbi->forward ) { // forward with no change to len or payload - mbuf.Send_msg( Message::NO_CHANGE, NULL ); + mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL ); } } @@ -148,8 +148,8 @@ void cb1( Message& mbuf, int mtype, int subid, int len, registered as the default callback; it counts the messages that we aren't giving details about. */ -void cbd( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { +void cbd( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { cb_info_t* cbi; if( (cbi = (cb_info_t *) data) == NULL ) { @@ -161,7 +161,7 @@ void cbd( Message& mbuf, int mtype, int subid, int len, if( cbi->forward ) { // forward with no change to len or payload - mbuf.Send_msg( Message::NO_CHANGE, NULL ); + mbuf.Send_msg( xapp::Message::NO_CHANGE, NULL ); } } @@ -192,7 +192,7 @@ int main( int argc, char** argv ) { cbi->forward = true; break; - case 'p': // define port + case 'p': // define port port = argv[ai+1]; ai++; break; diff --git a/examples/xapp_t1.cpp b/examples/xapp_t1.cpp index 51b3ba6..5ec8536 100644 --- a/examples/xapp_t1.cpp +++ b/examples/xapp_t1.cpp @@ -56,8 +56,8 @@ long cb1_lastts = 0; long cb1_lastc = 0; // respond with 2 messages for each type 1 received -void cb1( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { +void cb1( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { long now; long total_count; @@ -69,14 +69,14 @@ void cb1( Message& mbuf, int mtype, int subid, int len, } // just count messages -void cb2( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { +void cb2( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { cb2_count++; } // default to count all unrecognised messages -void cbd( Message& mbuf, int mtype, int subid, int len, - Msg_component payload, void* data ) { +void cbd( xapp::Message& mbuf, int mtype, int subid, int len, + xapp::Msg_component payload, void* data ) { cbd_count++; } diff --git a/examples/xapp_t2.cpp b/examples/xapp_t2.cpp index 67aa72f..1dc4da3 100644 --- a/examples/xapp_t2.cpp +++ b/examples/xapp_t2.cpp @@ -45,8 +45,8 @@ extern int main( int argc, char** argv ) { std::unique_ptr xfw; - std::unique_ptr msg; - Msg_component payload; // special type of unique pointer to the payload + std::unique_ptr msg; + xapp::Msg_component payload; // special type of unique pointer to the payload int sz; int len; @@ -108,7 +108,7 @@ extern int main( int argc, char** argv ) { len = snprintf( (char *) payload.get(), sz, "This is message %d\n", i ); // payload updated in place, prevent copy by passing nil - if ( ! msg->Send_msg( mtype, Message::NO_SUBID, len, NULL )) { + if ( ! msg->Send_msg( mtype, xapp::Message::NO_SUBID, len, NULL )) { fprintf( stderr, " send failed: %d\n", i ); } diff --git a/src/alarm/CMakeLists.txt b/src/alarm/CMakeLists.txt new file mode 100644 index 0000000..2bdde59 --- /dev/null +++ b/src/alarm/CMakeLists.txt @@ -0,0 +1,40 @@ +# vim: sw=4 ts=4 noet: +# +#================================================================================== +# Copyright (c) 2020 Nokia +# Copyright (c) 2020 AT&T Intellectual Property. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#================================================================================== +# + + +# For clarity: this generates object, not a lib as the CM command implies. +# +add_library( alarm_objects OBJECT + alarm.cpp +) + +target_include_directories (alarm_objects PUBLIC + $ + $ + PRIVATE src) + +# header files should go into .../include/xfcpp/ +if( DEV_PKG ) + install( FILES + alarm.hpp + DESTINATION ${install_inc} + ) +endif() + diff --git a/src/alarm/alarm.cpp b/src/alarm/alarm.cpp new file mode 100644 index 0000000..2648ed1 --- /dev/null +++ b/src/alarm/alarm.cpp @@ -0,0 +1,460 @@ +// 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: alarm.cpp + Abstract: This class provides an API to the alarm collector/reporter. + An object is insanced by the user xAPP allowing the xAPP + to send, clear, or resend the alarm as is necessary. + + + Date: 15 July 2020 + Author: E. Scott Daniels +*/ + +#include +#include +#include + +#include +#ifndef RIC_ALARM + #define RIC_ALARM 110 +#endif + +#include + +#include "msg_component.hpp" +#include "message.hpp" +#include "alarm.hpp" + +extern char* __progname; // runtime lib supplied since we don't get argv[0] + +namespace xapp { + + + +// ------ private ---------------------------------------------- + +/* + Suss out the alarm target from environment. + + We expect two variables in the environment: + ALARM_MGR_SERVICE_NAME + ALARM_MGR_SERVICE_PORT + + If name is not given, localhost is assumed. If port is not given + then we assume 4560 (the defacto RMR listen port). +*/ +static std::string endpoint_addr( ) { + char* et; // environment token + std::string addr = "localhost"; + std::string port = "4560"; + + if( (et = getenv( "ALARM_MGR_SERVICE_NAME" )) != NULL ) { + addr = std::string( et ); + } + + if( (et = getenv( "ALARM_MGR_SERVICE_PORT" )) != NULL ) { + port = std::string( et ); + } + + return addr + ":" + port; +} + + +/* + Return the current time in milliseconds past the UNIX epoch. +*/ +static long long now( void ) { + struct timespec ts; + long long now = 0; + + clock_gettime( CLOCK_REALTIME, &ts ); + now = (ts.tv_sec * 1000000) + (ts.tv_nsec/1000000); // convert nano to milli and bang onto seconds + + return now; +} + + +/* + Build the alarm json message with the current data. + Returns the length of the payload inserted. +*/ +int xapp::Alarm::build_alarm( int action_id, xapp::Msg_component payload, int payload_len ) { + //char wbuf[4096]; + std::string action; + int used; + + if( app_id.compare( "" ) == 0 ) { + app_id = std::string( __progname ); // see comment for extern above + } + + if( severity.compare( "" ) == 0 ) { + Set_severity( Alarm::SEV_WARN ); + } + + switch( action_id ) { + case Alarm::ACT_CLEAR: + action = "CLEAR"; + break; + + case Alarm::ACT_CLEAR_ALL: + action = "CLEARALL"; + break; + + default: + action = "RAISE"; + break; + } + + //memset( wbuf, 0, sizeof( wbuf ) ); + //snprintf( wbuf, sizeof( wbuf ), + used = snprintf( (char *) payload.get(), payload_len, + "{ " + "\"managedObjectId\": \"%s\", " + "\"applicationId\": \"%s\", " + "\"specificProblem\": %d, " + "\"perceivedSeverity\": \"%s\", " + "\"identifyingInfo\": \"%s\", " + "\"additionalInfo\": \"%s\", " + "\"AlarmAction\": \"%s\", " + "\"AlarmTime\": %lld" + " }", + + me_id.c_str(), + app_id.c_str(), + problem_id, + severity.c_str(), + info.c_str(), + add_info.c_str(), + action.c_str(), + now() + ); + + //action = std::string( wbuf ); + return used; +} + +// --------------- builders/operators ------------------------------------- + +/* + Create a new message wrapper for an existing RMR msg buffer. + + msg is a message which was allocaed by the framework and thus has the + mrc reference embedded. +*/ +xapp::Alarm::Alarm( std::shared_ptr msg ) : + msg( msg ), + endpoint( endpoint_addr() ), + whid( -1 ), + app_id( "" ), + me_id( "" ), + problem_id( -1 ), + info( "" ), + add_info( "" ), + action( "" ) +{ /* empty body */ } + +/* + Parameterised constructor (avoids calling setters after creation). +*/ +xapp::Alarm::Alarm( std::shared_ptr msg, int prob_id, std::string meid ) : + msg( msg ), + endpoint( endpoint_addr() ), + whid( -1 ), + app_id( "" ), + me_id( meid ), + problem_id( prob_id ), + info( "" ), + add_info( "" ), + action( "" ) +{ /* empty body */ } + +xapp::Alarm::Alarm( std::shared_ptr msg, std::string meid ) : + msg( msg ), + endpoint( endpoint_addr() ), + whid( -1 ), + app_id( "" ), + me_id( meid ), + problem_id( -1 ), + info( "" ), + add_info( "" ), + action( "" ) +{ /* empty body */ } + + + +// ------------------ copy and move support --------------------------------- + +/* + Copy builder. Given a source object instance (soi), create a copy. + Creating a copy should be avoided as it can be SLOW! +*/ +xapp::Alarm::Alarm( const Alarm& soi ) { + msg = soi.msg; + endpoint = soi.endpoint; + whid = soi.whid; + + me_id = soi.me_id; // user stuff + app_id = soi.app_id; + problem_id = soi.problem_id; + severity = soi.severity; + info = soi.info; + add_info = soi.add_info; +} + +/* + 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. +*/ +Alarm& xapp::Alarm::operator=( const Alarm& soi ) { + if( this != &soi ) { // cannot do self assignment + msg = soi.msg; + endpoint = soi.endpoint; + whid = soi.whid; + + me_id = soi.me_id; + app_id = soi.app_id; + problem_id = soi.problem_id; + severity = soi.severity; + info = soi.info; + add_info = soi.add_info; + } + + 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. +*/ +xapp::Alarm::Alarm( Alarm&& soi ) { + msg = soi.msg; // capture pointers and copy data before setting soruce things to nil + endpoint = soi.endpoint; + whid = soi.whid; + + me_id = soi.me_id; + app_id = soi.app_id; + problem_id = soi.problem_id; + severity = soi.severity; + info = soi.info; + add_info = soi.add_info; + + soi.msg = NULL; // prevent closing of RMR stuff on soi destroy +} + +/* + 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. +*/ +Alarm& xapp::Alarm::operator=( Alarm&& soi ) { + if( this != &soi ) { // cannot do self assignment + // anything that needs to be freed/delted from soi, must be done here + + msg = soi.msg; // move pointers and values + endpoint = soi.endpoint; + whid = soi.whid; + + me_id = soi.me_id; + app_id = soi.app_id; + problem_id = soi.problem_id; + severity = soi.severity; + info = soi.info; + add_info = soi.add_info; + + soi.msg = NULL; // prevent bad things when source is destroyed + } + + return *this; +} + +/* + Destroyer. +*/ +xapp::Alarm::~Alarm() { + + msg = NULL; +} + + +// ---- setters ------------------------------------------------- + +void xapp::Alarm::Set_meid( std::string new_meid ) { + me_id = new_meid; +} + +void xapp::Alarm::Set_severity( int new_sev ) { + switch( new_sev ) { + case Alarm::SEV_CRIT: + severity = "CRITICAL"; + break; + + case Alarm::SEV_MAJOR: + severity = "MAJOR"; + break; + + case Alarm::SEV_MINOR: + severity = "MINOR"; + break; + + case Alarm::SEV_WARN: + severity = "WARNING"; + break; + + case Alarm::SEV_CLEAR: + severity = "CLEARED"; + break; + + default: + severity = "DEFAULT"; + break; + } +} + +void xapp::Alarm::Set_appid( std::string new_id ) { + app_id = new_id; +} + +void xapp::Alarm::Set_problem( int new_id ) { + problem_id = new_id; +} + +void xapp::Alarm::Set_info( std::string new_info ) { + info = new_info; +} + +void xapp::Alarm::Set_additional( std::string new_info ) { + add_info = new_info; +} + +void xapp::Alarm::Set_whid( int new_whid ) { + whid = new_whid; +} + +void xapp::Alarm::Dump() { + fprintf( stderr, "Alarm: prob id: %d\n", problem_id ); + fprintf( stderr, "Alarm: meid: %s\n", me_id.c_str() ); + fprintf( stderr, "Alarm: app: %s\n", app_id.c_str() ); + fprintf( stderr, "Alarm: info: %s\n", info.c_str() ); + fprintf( stderr, "Alarm: ainfo: %s\n", add_info.c_str() ); +} + +// ------------------- getters ------------------------------------ + +/* + Return the enpoint address string we have. +*/ +std::string xapp::Alarm::Get_endpoint( ) { + return endpoint; +} + +// ------- message sending --------------------------------------- + +/* + Send a raise message with the alarm contents unchanged. +*/ +bool xapp::Alarm::Raise( ) { + int used; + used = build_alarm( ACT_RAISE, msg->Get_payload(), msg->Get_available_size() ); + msg->Wormhole_send( whid, RIC_ALARM, xapp::Message::NO_SUBID, used + 1, NULL ); +} + +/* + Additional prototypes allow for avoiding some setter calls when raising alarms. + Severity is one of our SEV_* constants. Problem is the caller's assigned + problem ID. Info and addional_info are user supplied data that is just passed + through. +*/ +bool xapp::Alarm::Raise( int severity, int problem, std::string info ) { + this->severity = severity; + problem_id = problem; + this->info = info; + + Raise(); +} + +bool xapp::Alarm::Raise( int severity, int problem, std::string info, std::string additional_info ) { + this->severity = severity; + problem_id = problem; + this->info = info; + this->add_info = additional_info; + + Raise(); +} + +/* + Send a clear message with the contents of the alarm otherwise unchanged. +*/ +bool xapp::Alarm::Clear( ) { + int used; + + used = build_alarm( ACT_CLEAR, msg->Get_payload(), msg->Get_available_size() ); + msg->Wormhole_send( whid, RIC_ALARM, xapp::Message::NO_SUBID, used + 1, NULL ); +} + +/* + Additional prototypes allow for avoiding some setter calls when raising alarms. + Severity is one of our SEV_* constants. Problem is the caller's assigned + problem ID. Info and addional_info are user supplied data that is just passed + through. +*/ +bool xapp::Alarm::Clear( int severity, int problem, std::string info ) { + this->severity = severity; + problem_id = problem; + this->info = info; + + Clear(); +} + +bool xapp::Alarm::Clear( int severity, int problem, std::string info, std::string additional_info ) { + this->severity = severity; + problem_id = problem; + this->info = info; + this->add_info = additional_info; + + Clear(); +} + + +/* + Send a clear-all message. The contents of the alarm are unaffected. +*/ +bool xapp::Alarm::Clear_all( ) { + int used; + + used = build_alarm( ACT_CLEAR_ALL, msg->Get_payload(), msg->Get_available_size() ); + msg->Wormhole_send( whid, RIC_ALARM, xapp::Message::NO_SUBID, used + 1, NULL ); +} + + +/* + This is a convenience function which sends a clear message followed by a + raise message. Alarm contents are not adjusted. +*/ +bool xapp::Alarm::Raise_again( ) { + Clear(); + Raise(); +} + + + +} // namespace diff --git a/src/alarm/alarm.hpp b/src/alarm/alarm.hpp new file mode 100644 index 0000000..539e385 --- /dev/null +++ b/src/alarm/alarm.hpp @@ -0,0 +1,111 @@ +// 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: alarm.hpp + Abstract: Headers for the alarm class. + This class provides for an alarm API. + + Date: 15 July 2020 + Author: E. Scott Daniels +*/ + +#ifndef _XAPP_ALARM_HPP +#define _XAPP_ALARM_HPP + + +#include +#include + +#include "msg_component.hpp" + +namespace xapp { + +// ------------------------------------------------------------------------ + +class Alarm { + private: + std::shared_ptr msg; // message to send + std::shared_ptr psp; // shared pointer to the payload to give out + std::string endpoint; // the ip:port addr:port of the alarm collector + int whid; + + // data for the payload + std::string me_id; // managed element ID + std::string app_id; // application ID + int problem_id; // problem ID (specific problem) + std::string severity; // SEV_* constants + std::string action; // ACT_* constants + std::string info; // info string supplied by user + std::string add_info; // additional information supplied by user + + int build_alarm( int action_id, xapp::Msg_component payload, int payload_len ); + + public: + static const int SEV_CRIT = 1; // allow translation to string on send/gen + static const int SEV_MAJOR = 2; + static const int SEV_MINOR = 3; + static const int SEV_WARN = 4; + static const int SEV_CLEAR = 5; + static const int SEV_DEFAULT = 6; + + static const int ACT_RAISE = 1; // action const map to alarm manager strings + static const int ACT_CLEAR = 2; + static const int ACT_CLEAR_ALL = 3; + + Alarm( std::shared_ptr msg ); // builders + Alarm( std::shared_ptr msg, std::string meid ); + Alarm( std::shared_ptr msg, int prob_id, std::string meid ); + + Alarm( const Alarm& soi ); // copy to newly created instance + Alarm& operator=( const Alarm& soi ); // copy operator + Alarm( Alarm&& soi ); // mover + Alarm& operator=( Alarm&& soi ); // move operator + ~Alarm(); // destroyer + + + std::string Get_endpoint( ); + + void Set_additional( std::string new_info ); + void Set_appid( std::string new_id ); + void Set_info( std::string new_info ); + void Set_meid( std::string new_meid ); + void Set_problem( int new_id ); + void Set_severity( int new_sev ); + void Set_whid( int whid ); + + + bool Raise( ); + bool Raise( int severity, int problem, std::string info ); + bool Raise( int severity, int problem, std::string info, std::string addional_info ); + bool Raise_again( ); + + bool Clear( ); + bool Clear( int severity, int problem, std::string info ); + bool Clear( int severity, int problem, std::string info, std::string addional_info ); + bool Clear_all( ); + + + void Dump(); +}; + +} // namespace + +#endif diff --git a/src/json/jhash.cpp b/src/json/jhash.cpp index ad0bd9c..df95e13 100644 --- a/src/json/jhash.cpp +++ b/src/json/jhash.cpp @@ -34,6 +34,10 @@ #include "jwrapper.h" #include "jhash.hpp" + +namespace xapp { + + // ------------------------------------------------------------------------ @@ -44,7 +48,7 @@ after which the functions provided by the class can be used to suss out the values. */ -Jhash::Jhash( const char* jbuf ) : +xapp::Jhash::Jhash( const char* jbuf ) : master_st( NULL ), st( jw_new( jbuf ) ) { /* empty body */ } @@ -79,7 +83,7 @@ Jhash& Jhash::operator=( Jhash&& soi ) { /* Blow it away. */ -Jhash::~Jhash() { +xapp::Jhash::~Jhash() { if( master_st != NULL ) { // revert blob set if needed st = master_st; } @@ -106,7 +110,7 @@ Jhash::~Jhash() { overlays with the named root; unset needs only to be called to return to the top level. */ -bool Jhash::Set_blob( const char* name ) { +bool xapp::Jhash::Set_blob( const char* name ) { void* bst; // blob symbol table if( master_st == NULL ) { // must capture master @@ -124,7 +128,7 @@ bool Jhash::Set_blob( const char* name ) { /* Return the suss root (blob root) to the root of the symtab. */ -void Jhash::Unset_blob( ) { +void xapp::Jhash::Unset_blob( ) { if( master_st != NULL ) { st = master_st; } @@ -138,14 +142,14 @@ void Jhash::Unset_blob( ) { Right now we don't have much to work with other than checking for a nil table. */ -bool Jhash::Parse_errors( ) { +bool xapp::Jhash::Parse_errors( ) { return st == NULL; } /* Dump the selected blob as much as we can. */ -void Jhash::Dump() { +void xapp::Jhash::Dump() { jw_dump( st ); } @@ -155,19 +159,19 @@ void Jhash::Dump() { These funcitons return true if the named object in the current blob is the indicated type */ -bool Jhash::Is_value( const char* name ) { +bool xapp::Jhash::Is_value( const char* name ) { return jw_is_value( st, name ) == 1; } -bool Jhash::Is_bool( const char* name ) { +bool xapp::Jhash::Is_bool( const char* name ) { return jw_is_bool( st, name ) == 1; } -bool Jhash::Is_null( const char* name ) { +bool xapp::Jhash::Is_null( const char* name ) { return jw_is_null( st, name ) == 1; } -bool Jhash::Is_string( const char* name ) { +bool xapp::Jhash::Is_string( const char* name ) { return jw_is_string( st, name ) == 1; } @@ -175,19 +179,19 @@ bool Jhash::Is_string( const char* name ) { These functions return true if the indicated element in the array is the indicated type. */ -bool Jhash::Is_string_ele( const char* name, int eidx ) { +bool xapp::Jhash::Is_string_ele( const char* name, int eidx ) { return jw_is_string_ele( st, name, eidx ) == 1; } -bool Jhash::Is_value_ele( const char* name, int eidx ) { +bool xapp::Jhash::Is_value_ele( const char* name, int eidx ) { return jw_is_value_ele( st, name, eidx ) == 1; } -bool Jhash::Is_bool_ele( const char* name, int eidx ) { +bool xapp::Jhash::Is_bool_ele( const char* name, int eidx ) { return jw_is_bool_ele( st, name, eidx ) == 1; } -bool Jhash::Is_null_ele( const char* name, int eidx ) { +bool xapp::Jhash::Is_null_ele( const char* name, int eidx ) { return jw_is_null_ele( st, name, eidx ) == 1; } @@ -196,14 +200,14 @@ bool Jhash::Is_null_ele( const char* name, int eidx ) { /* Returns true if the named element is in the hash. */ -bool Jhash::Exists( const char* name ) { +bool xapp::Jhash::Exists( const char* name ) { return jw_exists( st, name ) == 1; } /* Returns true if the named element is not in the hash. */ -bool Jhash::Is_missing( const char* name ) { +bool xapp::Jhash::Is_missing( const char* name ) { return jw_missing( st, name ) == 1; } @@ -215,7 +219,7 @@ bool Jhash::Is_missing( const char* name ) { Symtab saves bool values as 1 for true and doesn't provide a bool fetch function. So, fetch the value and return true if it is 1. */ -bool Jhash::Bool( const char* name ) { +bool xapp::Jhash::Bool( const char* name ) { int v; v = (int) jw_value( st, name ); return v == 1; @@ -225,7 +229,7 @@ bool Jhash::Bool( const char* name ) { Returns a C++ string to the named object; If the element is not in the hash an empty string is returned. */ -std::string Jhash::String( const char* name ) { +std::string xapp::Jhash::String( const char* name ) { std::string rv = ""; char* hashv; @@ -241,7 +245,7 @@ std::string Jhash::String( const char* name ) { Returns the value assocated with the named object; If the element is not in the hash 0 is returned. */ -double Jhash::Value( const char* name ) { +double xapp::Jhash::Value( const char* name ) { return jw_value( st, name ); } @@ -250,7 +254,7 @@ double Jhash::Value( const char* name ) { /* Return the length of the named array, or -1 if it doesn't exist. */ -int Jhash::Array_len( const char* name ) { +int xapp::Jhash::Array_len( const char* name ) { return jw_array_len( st, name ); } @@ -258,7 +262,7 @@ int Jhash::Array_len( const char* name ) { /* Sets the blob in the array [eidx] to the current reference blob. */ -bool Jhash::Set_blob_ele( const char* name, int eidx ) { +bool xapp::Jhash::Set_blob_ele( const char* name, int eidx ) { void* bst; if( (bst = jw_obj_ele( st, name, eidx )) != NULL ) { @@ -275,7 +279,7 @@ bool Jhash::Set_blob_ele( const char* name, int eidx ) { /* Return the string at index eidx in the array . */ -std::string Jhash::String_ele( const char* name, int eidx ) { +std::string xapp::Jhash::String_ele( const char* name, int eidx ) { std::string rv = ""; char* hashv; @@ -289,14 +293,17 @@ std::string Jhash::String_ele( const char* name, int eidx ) { /* Return the value at index eidx in the array . */ -double Jhash::Value_ele( const char* name, int eidx ) { +double xapp::Jhash::Value_ele( const char* name, int eidx ) { return jw_value_ele( st, name, eidx ); } /* Return the bool value at index eidx in the array . */ -bool Jhash::Bool_ele( const char* name, int eidx ) { +bool xapp::Jhash::Bool_ele( const char* name, int eidx ) { return jw_bool_ele( st, name, eidx ) == 1; } + + +} // namespace diff --git a/src/json/jhash.hpp b/src/json/jhash.hpp index 2963610..6ba1e50 100644 --- a/src/json/jhash.hpp +++ b/src/json/jhash.hpp @@ -34,6 +34,8 @@ #include +namespace xapp { + // ------------------------------------------------------------------------ class Jhash { @@ -83,4 +85,6 @@ class Jhash { }; + +} // namespace #endif diff --git a/src/messaging/callback.cpp b/src/messaging/callback.cpp index d4d697d..9fd603f 100644 --- a/src/messaging/callback.cpp +++ b/src/messaging/callback.cpp @@ -30,8 +30,8 @@ #include #include "message.hpp" -//class Messenger; +namespace xapp { /* Builder. @@ -49,10 +49,12 @@ Callback::Callback( user_callback ufun, void* data ) { // builder /* Drive_cb will invoke the callback and pass along the stuff passed here. */ -void Callback::Drive_cb( Message& m ) { +void xapp::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 ); } } + +} // namespace diff --git a/src/messaging/callback.hpp b/src/messaging/callback.hpp index b8e9dfa..0c892a8 100644 --- a/src/messaging/callback.hpp +++ b/src/messaging/callback.hpp @@ -31,11 +31,15 @@ #include +#include "msg_component.hpp" + +namespace xapp { + + 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 ); +typedef void(*user_callback)( xapp::Message& m, int mtype, int subid, int payload_len, xapp::Msg_component payload, void* usr_data ); class Callback { @@ -48,5 +52,7 @@ class Callback { void Drive_cb( Message& m ); // invoker }; +} // namespace #endif + diff --git a/src/messaging/default_cb.cpp b/src/messaging/default_cb.cpp index 1083293..2156ad3 100644 --- a/src/messaging/default_cb.cpp +++ b/src/messaging/default_cb.cpp @@ -38,6 +38,9 @@ #include "messenger.hpp" +namespace xapp { + + /* This is the default health check function that we provide (user may override it). It will respond to health check messages by @@ -52,3 +55,6 @@ void Health_ck_cb( Message& mbuf, int mtype, int sid, int len, Msg_component pay snprintf( (char* ) response, sizeof( response ), "OK\n" ); mbuf.Send_response( RIC_HEALTH_CHECK_RESP, sid, strlen( (char *) response )+1, response ); } + + +} // namespace diff --git a/src/messaging/default_cb.hpp b/src/messaging/default_cb.hpp index 2e96003..7f9d1ad 100644 --- a/src/messaging/default_cb.hpp +++ b/src/messaging/default_cb.hpp @@ -32,8 +32,12 @@ #ifndef _DEF_CB_H #define _DEF_CB_H +namespace xapp { void Health_ck_cb( Message& mbuf, int mtype, int sid, int len, Msg_component payload, void* data ); + +} // namespace + #endif diff --git a/src/messaging/message.cpp b/src/messaging/message.cpp index 30a0fbc..41f5a78 100644 --- a/src/messaging/message.cpp +++ b/src/messaging/message.cpp @@ -41,6 +41,10 @@ #include "message.hpp" +namespace xapp { + + + // --------------- private ------------------------------------------------ // --------------- builders/operators ------------------------------------- @@ -48,12 +52,12 @@ /* Create a new message wrapper for an existing RMR msg buffer. */ -Message::Message( rmr_mbuf_t* mbuf, void* mrc ) { +xapp::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 ) { +xapp::Message::Message( void* mrc, int payload_len ) { this->mrc = mrc; this->mbuf = rmr_alloc_msg( mrc, payload_len ); } @@ -62,29 +66,29 @@ Message::Message( void* mrc, int 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 ) { +xapp::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 ); + 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 ) { +Message& xapp::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 ); + mbuf = rmr_realloc_payload( soi.mbuf, payload_size, RMR_COPY, RMR_CLONE ); } return *this; @@ -95,7 +99,7 @@ Message& Message::operator=( const Message& soi ) { the soi ensuring that the destriction of the soi doesn't trash things from under us. */ -Message::Message( Message&& soi ) { +xapp::Message::Message( Message&& soi ) { mrc = soi.mrc; mbuf = soi.mbuf; @@ -108,12 +112,12 @@ Message::Message( Message&& soi ) { ensure the object reference is cleaned up, and ensuring that the source object references are removed. */ -Message& Message::operator=( Message&& soi ) { +Message& xapp::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; @@ -128,7 +132,7 @@ Message& Message::operator=( Message&& soi ) { /* Destroyer. */ -Message::~Message() { +xapp::Message::~Message() { if( mbuf != NULL ) { rmr_free_msg( mbuf ); } @@ -147,7 +151,7 @@ Message::~Message() { This function will return a NULL pointer if malloc fails. */ //char* Message::Copy_payload( ){ -std::unique_ptr Message::Copy_payload( ){ +std::unique_ptr xapp::Message::Copy_payload( ){ unsigned char* new_payload = NULL; if( mbuf != NULL ) { @@ -161,7 +165,7 @@ std::unique_ptr Message::Copy_payload( ){ /* Makes a copy of the MEID and returns a smart pointer to it. */ -std::unique_ptr Message::Get_meid(){ +std::unique_ptr xapp::Message::Get_meid(){ unsigned char* m = NULL; m = (unsigned char *) malloc( sizeof( unsigned char ) * RMR_MAX_MEID ); @@ -176,11 +180,11 @@ std::unique_ptr Message::Get_meid(){ If mbuf isn't valid (nil, or message has a broken header) the return will be -1. */ -int Message::Get_available_size(){ +int xapp::Message::Get_available_size(){ return rmr_payload_size( mbuf ); // rmr can handle a nil pointer } -int Message::Get_mtype(){ +int xapp::Message::Get_mtype(){ int rval = INVALID_MTYPE; if( mbuf != NULL ) { @@ -193,7 +197,7 @@ int Message::Get_mtype(){ /* Makes a copy of the source field and returns a smart pointer to it. */ -std::unique_ptr Message::Get_src(){ +std::unique_ptr xapp::Message::Get_src(){ unsigned char* m = NULL; m = (unsigned char *) malloc( sizeof( unsigned char ) * RMR_MAX_SRC ); @@ -206,7 +210,7 @@ std::unique_ptr Message::Get_src(){ return std::unique_ptr( m ); } -int Message::Get_state( ){ +int xapp::Message::Get_state( ){ int state = INVALID_STATUS; if( mbuf != NULL ) { @@ -216,7 +220,7 @@ int Message::Get_state( ){ return state; } -int Message::Get_subid(){ +int xapp::Message::Get_subid(){ int rval = INVALID_SUBID; if( mbuf != NULL ) { @@ -230,7 +234,7 @@ int Message::Get_subid(){ 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 xapp::Message::Get_len(){ int rval = 0; if( mbuf != NULL ) { @@ -248,7 +252,7 @@ int Message::Get_len(){ length by calling Message:Get_available_size(), and ensuring that writing beyond the indicated size does not happen. */ -Msg_component Message::Get_payload(){ +Msg_component xapp::Message::Get_payload(){ if( mbuf != NULL ) { return std::unique_ptr( mbuf->payload ); } @@ -256,25 +260,25 @@ Msg_component Message::Get_payload(){ return NULL; } -void Message::Set_meid( std::shared_ptr new_meid ) { +void xapp::Message::Set_meid( std::shared_ptr new_meid ) { if( mbuf != NULL ) { rmr_str2meid( mbuf, (unsigned char *) new_meid.get() ); } } -void Message::Set_mtype( int new_type ){ +void xapp::Message::Set_mtype( int new_type ){ if( mbuf != NULL ) { mbuf->mtype = new_type; } } -void Message::Set_len( int new_len ){ +void xapp::Message::Set_len( int new_len ){ if( mbuf != NULL && new_len >= 0 ) { mbuf->len = new_len; } } -void Message::Set_subid( int new_subid ){ +void xapp::Message::Set_subid( int new_subid ){ if( mbuf != NULL ) { mbuf->sub_id = new_subid; } @@ -288,7 +292,7 @@ void Message::Set_subid( int new_subid ){ 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 xapp::Message::Send( ) { bool state = false; if( mbuf != NULL ) { @@ -303,7 +307,7 @@ bool Message::Send( ) { 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 xapp::Message::Reply( ) { bool state = false; if( mbuf != NULL ) { @@ -325,7 +329,7 @@ bool Message::Reply( ) { 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 xapp::Message::Send( int mtype, int subid, int payload_len, unsigned char* payload, int stype, rmr_whid_t whid ) { bool state = false; if( mbuf != NULL ) { @@ -349,10 +353,18 @@ bool Message::Send( int mtype, int subid, int payload_len, unsigned char* payloa memcpy( mbuf->payload, payload, mbuf->len ); } - if( stype == RESPONSE ) { - mbuf = rmr_rts_msg( mrc, mbuf ); - } else { - mbuf = rmr_send_msg( mrc, mbuf ); + switch( stype ) { + case RESPONSE: + mbuf = rmr_rts_msg( mrc, mbuf ); + break; + + case MESSAGE: + mbuf = rmr_send_msg( mrc, mbuf ); + break; + + case WORMHOLE_MSG: + mbuf = rmr_wh_send_msg( mrc, whid, mbuf ); + break; } state = mbuf->state == RMR_OK; @@ -370,23 +382,23 @@ bool Message::Send( int mtype, int subid, int payload_len, unsigned char* payloa 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 response ) { - return Send( mtype, subid, response_len, response.get(), RESPONSE ); +bool xapp::Message::Send_response( int mtype, int subid, int response_len, std::shared_ptr response ) { + return Send( mtype, subid, response_len, response.get(), RESPONSE, NO_WHID ); } -bool Message::Send_response( int mtype, int subid, int response_len, unsigned char* response ) { - return Send( mtype, subid, response_len, response, RESPONSE ); +bool xapp::Message::Send_response( int mtype, int subid, int response_len, unsigned char* response ) { + return Send( mtype, subid, response_len, response, RESPONSE, NO_WHID ); } /* These allow a response message to be sent without changing the mtype/subid. */ -bool Message::Send_response( int response_len, std::shared_ptr response ) { - return Send( NO_CHANGE, NO_CHANGE, response_len, response.get(), RESPONSE ); +bool xapp::Message::Send_response( int response_len, std::shared_ptr response ) { + return Send( NO_CHANGE, NO_CHANGE, response_len, response.get(), RESPONSE, NO_WHID ); } -bool Message::Send_response( int response_len, unsigned char* response ) { - return Send( NO_CHANGE, NO_CHANGE, response_len, response, RESPONSE ); +bool xapp::Message::Send_response( int response_len, unsigned char* response ) { + return Send( NO_CHANGE, NO_CHANGE, response_len, response, RESPONSE, NO_WHID ); } @@ -399,21 +411,35 @@ bool Message::Send_response( int response_len, unsigned char* response ) { 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 payload ) { - return Send( mtype, subid, payload_len, payload.get(), MESSAGE ); +bool xapp::Message::Send_msg( int mtype, int subid, int payload_len, std::shared_ptr payload ) { + return Send( mtype, subid, payload_len, payload.get(), MESSAGE, NO_WHID ); } -bool Message::Send_msg( int mtype, int subid, int payload_len, unsigned char* payload ) { - return Send( mtype, subid, payload_len, payload, MESSAGE ); +bool xapp::Message::Send_msg( int mtype, int subid, int payload_len, unsigned char* payload ) { + return Send( mtype, subid, payload_len, payload, MESSAGE, NO_WHID ); } /* Similar send functions that allow the message type/subid to remain unchanged */ -bool Message::Send_msg( int payload_len, std::shared_ptr payload ) { - return Send( NO_CHANGE, NO_CHANGE, payload_len, payload.get(), MESSAGE ); +bool xapp::Message::Send_msg( int payload_len, std::shared_ptr payload ) { + return Send( NO_CHANGE, NO_CHANGE, payload_len, payload.get(), MESSAGE, NO_WHID ); } -bool Message::Send_msg( int payload_len, unsigned char* payload ) { - return Send( NO_CHANGE, NO_CHANGE, payload_len, payload, MESSAGE ); +bool xapp::Message::Send_msg( int payload_len, unsigned char* payload ) { + return Send( NO_CHANGE, NO_CHANGE, payload_len, payload, MESSAGE, NO_WHID ); } + + +/* + Wormhole send allows an xAPP to send a message directly based on an existing + wormhole ID (use xapp::Wormhole_open() to get one). Wormholes should NOT be + used for reponse messages, but are intended for things like route tables and + alarm messages where routing doesn't exist/apply. +*/ +bool xapp::Message::Wormhole_send( int whid, int mtype, int subid, int payload_len, std::shared_ptr payload ) { + return Send( mtype, subid, payload_len, payload.get(), WORMHOLE_MSG, whid ); +} + + +} // namespace diff --git a/src/messaging/message.hpp b/src/messaging/message.hpp index 0db0927..df71d88 100644 --- a/src/messaging/message.hpp +++ b/src/messaging/message.hpp @@ -49,6 +49,8 @@ #endif +namespace xapp { + // ------------------------------------------------------------------------ class Message { @@ -59,6 +61,7 @@ class Message { public: static const int NO_CHANGE = -99; // indicates no change to a send/reply parameter + static const int NO_WHID = -1; // no wormhole id applies static const int INVALID_MTYPE = -1; static const int INVALID_STATUS = -1; static const int INVALID_SUBID = -2; @@ -66,6 +69,7 @@ class Message { static const int RESPONSE = 0; // send types static const int MESSAGE = 1; + static const int WORMHOLE_MSG = 2; Message( rmr_mbuf_t* mbuf, void* mrc ); // builders Message( void* mrc, int payload_len ); @@ -93,7 +97,7 @@ class Message { bool Reply( ); bool Send( ); - bool Send( int mtype, int subid, int payload_len, unsigned char* payload, int stype ); + bool Send( int mtype, int subid, int payload_len, unsigned char* payload, int stype, int whid ); bool Send_msg( int mtype, int subid, int payload_len, std::shared_ptr payload ); bool Send_msg( int mtype, int subid, int payload_len, unsigned char* payload ); @@ -104,7 +108,10 @@ class Message { bool Send_response( int mtype, int subid, int payload_len, unsigned char* response ); bool Send_response( int payload_len, std::shared_ptr response ); bool Send_response( int payload_len, unsigned char* response ); + + bool Wormhole_send( int whid, int mtype, int subid, int payload_len, std::shared_ptr payload ); }; +} // namespace #endif diff --git a/src/messaging/messenger.cpp b/src/messaging/messenger.cpp index 4e5a278..67d5e2d 100644 --- a/src/messaging/messenger.cpp +++ b/src/messaging/messenger.cpp @@ -43,14 +43,16 @@ #include "default_cb.hpp" // default callback prototypes #include "message.hpp" #include "messenger.hpp" +#include "alarm.hpp" +namespace xapp { // --------------- private ----------------------------------------------------- // ---------------- C++ buggerd up way of maintining class constants ---------- -const int Messenger::MAX_PAYLOAD = (1024*64); -const int Messenger::DEFAULT_CALLBACK = -1; +const int xapp::Messenger::MAX_PAYLOAD = (1024*64); +const int xapp::Messenger::DEFAULT_CALLBACK = -1; // --------------- builders ----------------------------------------------- /* @@ -60,7 +62,7 @@ const int Messenger::DEFAULT_CALLBACK = -1; If port is nil, then the default port is used (4560). */ -Messenger::Messenger( const char* uport, bool wait4table ) { +xapp::Messenger::Messenger( const char* uport, bool wait4table ) { if( uport == NULL ) { listen_port = strdup( "4560" ); @@ -87,7 +89,7 @@ Messenger::Messenger( const char* uport, bool wait4table ) { 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 ) { +xapp::Messenger::Messenger( Messenger&& soi ) { mrc = soi.mrc; listen_port = soi.listen_port; ok_2_run = soi.ok_2_run; @@ -103,7 +105,7 @@ Messenger::Messenger( Messenger&& soi ) { 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 ) { +xapp::Messenger& Messenger::operator=( Messenger&& soi ) { if( this != &soi ) { // cannot move onto ourself if( mrc != NULL ) { rmr_close( mrc ); @@ -129,7 +131,7 @@ Messenger& Messenger::operator=( Messenger&& soi ) { /* Destroyer. */ -Messenger::~Messenger() { +xapp::Messenger::~Messenger() { if( mrc != NULL ) { rmr_close( mrc ); } @@ -151,7 +153,7 @@ Messenger::~Messenger() { 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 ) { +void xapp::Messenger::Add_msg_cb( int mtype, user_callback fun_name, void* data ) { Callback* cb; cb = new Callback( fun_name, data ); @@ -165,18 +167,57 @@ void Messenger::Add_msg_cb( int mtype, user_callback fun_name, void* data ) { 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 Messenger::Alloc_msg( int payload_size ) { +std::unique_ptr xapp::Messenger::Alloc_msg( int payload_size ) { return std::unique_ptr( new Message( mrc, payload_size ) ); } -void Messenger::Listen( ) { + +// ----------------- alarm support ----------------------------------------------- +/* + Allocate an alarm object. + Alarms must be allocated via the framework becasue we need a wormhole + id and to get one of those we need the mrc. We can easily send with + just a message, but to avoid having the user pass the framework + object in, we'll just supply a "factory" function. +*/ +std::unique_ptr xapp::Messenger::Alloc_alarm( int prob_id, std::string meid ) { + std::shared_ptr m; + Alarm* a; + + m = Alloc_msg( 4096 ); + a = new Alarm( m, prob_id, meid ); + a->Set_whid( Wormhole_open( a->Get_endpoint() ) ); + + return std::unique_ptr( a ); +} + +std::unique_ptr xapp::Messenger::Alloc_alarm( std::string meid ) { + return Alloc_alarm( -1, meid ); +} + +std::unique_ptr xapp::Messenger::Alloc_alarm( ) { + return Alloc_alarm( -1, "" ); +} + + +// ------------------- listening support ----------------------------------------------- + +/* + The Listen function waits for messages and drives the appropriate callback + function when one is received. This function will return to the caller + only when the ok to run flag in the object has been set to false (likely + never, or only at graceful termination). Callers should normally not + expect to have controll returned in the calling thread. + + Concurrently executing listeners are allowed. +*/ +void xapp::Messenger::Listen( ) { int count = 0; rmr_mbuf_t* mbuf = NULL; std::map::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_ptrm; + std::unique_ptr m; if( mrc == NULL ) { return; @@ -212,8 +253,9 @@ void Messenger::Listen( ) { /* Wait for the next message, up to a max timout, and return the message received. + This function allows the user xAPP to implement their own polling loop (no callbacks). */ -std::unique_ptr Messenger::Receive( int timeout ) { +std::unique_ptr xapp::Messenger::Receive( int timeout ) { rmr_mbuf_t* mbuf = NULL; std::unique_ptr m = NULL; @@ -230,7 +272,7 @@ std::unique_ptr Messenger::Receive( int timeout ) { /* Called to gracefully stop all listeners. */ -void Messenger::Stop( ) { +void xapp::Messenger::Stop( ) { ok_2_run = false; } @@ -238,7 +280,7 @@ void Messenger::Stop( ) { 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 ) { +void xapp::Messenger::Release_mbuf( void* vmbuf ) { rmr_free_msg( (rmr_mbuf_t *) vmbuf ); } */ @@ -262,7 +304,7 @@ void Messenger::Release_mbuf( void* vmbuf ) { 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 xapp::Messenger::Wait_for_cts( int max_wait ) { bool block_4ever; bool state = false; @@ -279,3 +321,17 @@ bool Messenger::Wait_for_cts( int max_wait ) { return state; } + +/* + Open a wormhole to the indicated endpoint and return the wormhole ID. +*/ +int xapp::Messenger::Wormhole_open( std::string endpoint ) { + rmr_whid_t whid; + + whid = rmr_wh_open( mrc, endpoint.c_str() ); + + return (int) whid; +} + + +} // namespace diff --git a/src/messaging/messenger.hpp b/src/messaging/messenger.hpp index b6e46b6..173426c 100644 --- a/src/messaging/messenger.hpp +++ b/src/messaging/messenger.hpp @@ -40,12 +40,16 @@ #include #include "message.hpp" +#include "alarm.hpp" #ifndef RMR_FALSE #define RMR_FALSE 0 #define RMR_TRUE 1 #endif +namespace xapp { + + class Messenger { private: @@ -57,7 +61,7 @@ class Messenger { 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( const Messenger& soi ); Messenger& operator=( const Messenger& soi ); public: @@ -71,12 +75,21 @@ class Messenger { ~Messenger(); // destroyer void Add_msg_cb( int mtype, user_callback fun_name, void* data ); + std::unique_ptr Alloc_msg( int payload_size ); // message allocation + + std::unique_ptr Alloc_alarm( ); // alarm allocation + std::unique_ptr Alloc_alarm( std::string meid ); + std::unique_ptr Alloc_alarm( int prob_id, std::string meid ); + void Listen( ); // lisen driver std::unique_ptr Receive( int timeout ); // receive 1 message void Stop( ); // force to stop - //void Release_mbuf( void* vmbuf ); bool Wait_for_cts( int max_wait ); + + int Wormhole_open( std::string endpoint ); }; + +} // namespace #endif diff --git a/src/messaging/msg_component.hpp b/src/messaging/msg_component.hpp index d707257..40662a2 100644 --- a/src/messaging/msg_component.hpp +++ b/src/messaging/msg_component.hpp @@ -35,6 +35,8 @@ #include +namespace xapp { + // -------------- smart pointer support -------------------------------- /* Pointers to a lot of things in the RMR message aren't directly @@ -54,4 +56,6 @@ typedef struct { */ using Msg_component = std::unique_ptr; + +} // namespace #endif diff --git a/src/xapp/xapp.hpp b/src/xapp/xapp.hpp index 5ba47ec..a00a1dc 100644 --- a/src/xapp/xapp.hpp +++ b/src/xapp/xapp.hpp @@ -42,7 +42,7 @@ #include "callback.hpp" #include "messenger.hpp" -class Xapp : public Messenger { +class Xapp : public xapp::Messenger { private: std::string name; diff --git a/test/Makefile b/test/Makefile index 4245d3b..691f365 100644 --- a/test/Makefile +++ b/test/Makefile @@ -11,7 +11,7 @@ rmr_em.o:: rmr_em.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 + g++ -g $(coverage_opts) -I ../src/alarm -I ../src/messaging unit_test.cpp -o unit_test rmr_em.o -lpthread # build a special jwrapper object with coverage settings jwrapper_test.o:: ../src/json/jwrapper.c ../src/json/jwrapper.h diff --git a/test/jhash_test.cpp b/test/jhash_test.cpp index 972e6e6..028fd11 100644 --- a/test/jhash_test.cpp +++ b/test/jhash_test.cpp @@ -80,7 +80,7 @@ static char* read_jstring( char* fname ) { int main( int argc, char** argv ) { int errors = 0; - Jhash* jh; + xapp::Jhash* jh; char* jstr; std::string sval; double val; @@ -94,7 +94,7 @@ int main( int argc, char** argv ) { fprintf( stderr, "read: (%s)\n", jstr ); - jh = new Jhash( jstr ); + jh = new xapp::Jhash( jstr ); free( jstr ); if( jh == NULL ) { @@ -240,9 +240,9 @@ int main( int argc, char** argv ) { // ----- jhashes can be moved, drive that logic for coverage - Jhash j2( "{}" ); + xapp::Jhash j2( "{}" ); - Jhash j1 = std::move( *jh ); // drives move constructor function + xapp::Jhash j1 = std::move( *jh ); // drives move constructor function j2 = std::move( j1 ); // drives move operator function @@ -251,18 +251,18 @@ int main( int argc, char** argv ) { fprintf( stderr, " testing for failures; jwrapper error and warning messages expected\n" ); // ---- these shouild all fail to parse, generate warnings to stderr, and drive error handling coverage ---- - jh = new Jhash( (char *) "{ \"bad\": [ [ 1, 2, 3 ], [ 3, 4, 5]] }" ); // drive the exception process for bad json + jh = new xapp::Jhash( (char *) "{ \"bad\": [ [ 1, 2, 3 ], [ 3, 4, 5]] }" ); // drive the exception process for bad json delete jh; - jh = new Jhash( (char *) " \"bad\": 5 }" ); // no opening brace + jh = new xapp::Jhash( (char *) " \"bad\": 5 }" ); // no opening brace state = jh->Parse_errors(); errors += fail_if( !state, "parse errors check returned false when known errors exist" ); delete jh; - jh = new Jhash( (char *) "{ \"bad\": fred }" ); // no quotes + jh = new xapp::Jhash( (char *) "{ \"bad\": fred }" ); // no quotes delete jh; - jh = new Jhash( (char *) "{ \"bad: 456, \"good\": 100 }" ); // missing quote; impossible to detect error + jh = new xapp::Jhash( (char *) "{ \"bad: 456, \"good\": 100 }" ); // missing quote; impossible to detect error jh->Dump(); // but dump should provide details fprintf( stderr, " good value=%d\n", (int) val ); delete jh; diff --git a/test/rmr_em.c b/test/rmr_em.c index c9f85cf..9d5c621 100644 --- a/test/rmr_em.c +++ b/test/rmr_em.c @@ -22,7 +22,7 @@ Mnemonic: rmr_em.c Abstract: RMR emulation for testing - Date: 20 March + Date: 20 March Author: E. Scott Daniels */ @@ -115,7 +115,7 @@ char* rmr_get_meid( rmr_mbuf_t* mbuf, char* m ) { if( mbuf != NULL ) { if( m == NULL ) { - m = (char *) malloc( sizeof( char ) * 32 ); + m = (char *) malloc( sizeof( char ) * 32 ); } h = (header_t *) mbuf->tp_buf; memcpy( m, h->meid, 32 ); @@ -138,7 +138,7 @@ char *rmr_get_src( rmr_mbuf_t* mbuf, char *m ) { if( mbuf != NULL ) { if( m == NULL ) { - m = (char *) malloc( sizeof( char ) * 32 ); + m = (char *) malloc( sizeof( char ) * 32 ); } h = (header_t *) mbuf->tp_buf; memcpy( m, h->src, 32 ); @@ -170,18 +170,18 @@ int rmr_str2meid( rmr_mbuf_t* mbuf, unsigned char* s ) { rmr_mbuf_t* rmr_send_msg( void* mrc, rmr_mbuf_t* mbuf ) { if( mbuf != NULL ) { - mbuf->state = 0; + mbuf->state = 0; } - + return mbuf; } rmr_mbuf_t* rmr_rts_msg( void* mrc, rmr_mbuf_t* mbuf ) { if( mbuf != NULL ) { - mbuf->state = 0; + mbuf->state = 0; } - + return mbuf; } @@ -248,8 +248,43 @@ int rmr_ready( void* mrc ) { if( ! state ) { state = 1; return 0; - } + } return 1; } +// ----------------------- wormhole dummies --------------------------------------------- + +typedef int rmr_whid_t; + +extern rmr_whid_t rmr_wh_open( void* vctx, char const* target ) { + static int whid = 0; + + if( vctx == NULL ) { + return -1; + } + return whid++; +} + +//extern rmr_mbuf_t* rmr_wh_call( void* vctx, rmr_whid_t whid, rmr_mbuf_t* msg, int call_id, int max_wait ); + +extern rmr_mbuf_t* rmr_wh_send_msg( void* vctx, rmr_whid_t whid, rmr_mbuf_t* mbuf ) { + if( mbuf != NULL ) { + if( whid >= 0 ) { + mbuf->state = 0; + } + + mbuf->state = 7; + } + + return mbuf; +} + +extern int rmr_wh_state( void* vctx, rmr_whid_t whid ) { + return whid >= 0; +} + +extern void rmr_wh_close( void* vctx, int whid ){ + return; +} + diff --git a/test/unit_test.cpp b/test/unit_test.cpp index b0612d8..56f40b8 100644 --- a/test/unit_test.cpp +++ b/test/unit_test.cpp @@ -20,15 +20,15 @@ /* Mnemonic: Unit_test.cpp - Abstract: This is the unit test driver for the C++ xAPP framework. It + 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 + 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 + Author: E. Scott Daniels */ #include @@ -39,14 +39,19 @@ #include "../src/messaging/message.hpp" #include "../src/messaging/messenger.hpp" #include "../src/messaging/msg_component.hpp" +#include "../src/alarm/alarm.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/alarm/alarm.cpp" #include "../src/xapp/xapp.cpp" +#include "ut_support.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. @@ -60,25 +65,25 @@ int good_cb2 = 0; int good_cbd = 0; /* - callback functions to register; driven as we "receive" messages (the RMR emulation package + 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 ) { +void cb1( xapp::Message& mbuf, int mtype, int subid, int len, xapp::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 ) { +void cb2( xapp::Message& mbuf, int mtype, int subid, int len, xapp::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 +void cbd( xapp::Message& mbuf, int mtype, int subid, int len, xapp::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, " cbd: bad message type: %d\n", mtype ); } @@ -90,8 +95,8 @@ void cbd( Message& mbuf, int mtype, int subid, int len, Msg_component payload, /* 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 + 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. */ @@ -104,9 +109,9 @@ void killer( std::shared_ptr x ) { 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 msg; + std::unique_ptr msg; std::shared_ptr x; - Msg_component payload; + xapp::Msg_component payload; std::unique_ptr ucs; unsigned char* new_payload; std::shared_ptr new_p_ref; // reference to payload to pass to send functions @@ -125,8 +130,8 @@ int main( int argc, char** argv ) { } switch( argv[ai][1] ) { // we only support -x so -xy must be -x -y - case 'p': - port = argv[ai+1]; + case 'p': + port = argv[ai+1]; ai++; break; @@ -138,7 +143,10 @@ int main( int argc, char** argv ) { ai++; } - + + set_test_name( "unit_test" ); + + // ------------------- generic xapp tests ---------------------------------------------- x = std::shared_ptr( new Xapp( port, true ) ); x->Add_msg_cb( 1, cb1, NULL ); x->Add_msg_cb( 2, cb2, NULL ); @@ -243,21 +251,21 @@ int main( int argc, char** argv ) { } // ----- 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 ); + + xapp::Messenger m1( (char *) "1234", false ); // messenger class does NOT permit copies, so no need to test + xapp::Messenger m2( (char *) "9999", false ); m1 = std::move( m2 ); // drives move operator= function - Messenger m3 = std::move( m1 ); // drives move constructor function + xapp::Messenger m3 = std::move( m1 ); // drives move constructor function - std::unique_ptr msg2 = x->Alloc_msg( 2048 ); - std::unique_ptr msg3 = x->Alloc_msg( 4096 ); + std::unique_ptr msg2 = x->Alloc_msg( 2048 ); + std::unique_ptr 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, " set string (%s) \n", (char *) (msg3->Get_payload()).get() ); - Message msg4 = *(msg3.get()); // drive copy builder; msg4 should have a 4096 byte payload + xapp::Message msg4 = *(msg3.get()); // drive copy builder; msg4 should have a 4096 byte payload fprintf( stderr, " copy string (%s) \n", (char *) (msg4.Get_payload()).get() ); // and payload should be coppied if( msg4.Get_available_size() != 4096 ) { errors++; @@ -281,7 +289,7 @@ int main( int argc, char** argv ) { fprintf( stderr, " message copy builder payload of copy not the expected string\n" ); } - Message msg5 = std::move( *(msg3.get()) ); // drive move constructor + xapp::Message msg5 = std::move( *(msg3.get()) ); // drive move constructor if( msg5.Get_available_size() != 2048 ) { errors++; fprintf( stderr, " message copy operator payload size smells: expected 2048, got %d\n", msg5.Get_available_size() ); @@ -298,5 +306,58 @@ int main( int argc, char** argv ) { fprintf( stderr, " message move operator payload len smells: expected 21, got %d\n", msg5.Get_len() ); } + // --------------------- alarm testing ------------------------------------------ + std::shared_ptr a; + + a = x->Alloc_alarm( ); // drive all possible constructors through the framework + errors += fail_if( a == NULL, "unable to allcoate a generic alarm" ); + + setenv( "ALARM_MGR_SERVICE_NAME", "alarm_svc", 1 ); + setenv( "ALARM_MGR_SERVICE_PORT", "9999", 1 ); + + a = x->Alloc_alarm( "meid-123" ); + errors += fail_if( a == NULL, "unable to allcoate an alarm with just meid" ); + + a = x->Alloc_alarm( 13, "meid-abc" ); + errors += fail_if( a == NULL, "unable to allcoate an alarm with meid and problem id" ); + + + //#####: 210:xapp::Alarm::Alarm( const Alarm& soi ) { + //#####: 227:Alarm& xapp::Alarm::operator=( const Alarm& soi ) { + //#####: 249:xapp::Alarm::Alarm( Alarm&& soi ) { + //#####: 269:Alarm& xapp::Alarm::operator=( Alarm&& soi ) { + + a->Set_meid( "changed_meid" ); + for( i = 0; i < 6; i++ ) { + a->Set_severity( i ); // drive all switch possibilities + } + + a->Set_appid( "new-appid" ); + a->Set_problem( 99 ); + a->Set_info( "new information string" ); + a->Set_additional( "new additional information string" ); + + a->Dump(); + errors += fail_if( !a->Raise(), "alarm raise with no parms failed" ); + errors += fail_if( !a->Raise( xapp::Alarm::SEV_CRIT, 15, "problem string" ), "alarm raise s/p/i failed" ); + errors += fail_if( !a->Raise( xapp::Alarm::SEV_CRIT, 15, "problem string", "addtl info" ), "alarm raise s/p/i/a failed" ); + + errors += fail_if( !a->Clear( ), "clear alarm failed" ); + errors += fail_if( !a->Clear( xapp::Alarm::SEV_CRIT, 15, "problem string" ), "alarm clear s/p/i failed" ); + errors += fail_if( !a->Clear( xapp::Alarm::SEV_CRIT, 15, "problem string", "addtl info" ), "alarm clear s/p/i/a failed" ); + errors += fail_if( !a->Clear_all( ), "clear all failed" ); + + errors += fail_if( !a->Raise_again( ), "alarm raise again failed" ); + + xapp::Alarm b = *a.get(); // force the move/copy operator functions to trigger + xapp::Alarm c( NULL ); // a useless alarm without a message + xapp::Alarm f( NULL, "meid" ); // a useless alarm to drive direct construction + c = *a.get(); // drive copy = operator + + b = std::move( c ); // move = operator + xapp::Alarm d = std::move( b ); // move constructor + + announce_results( errors ); return errors > 0; } + -- 2.16.6