--- /dev/null
+
+This directory contains documentation source which generates
+some (if not all) of the documentation that is put into the
+directory scraped for RTD. One of the advantages of this
+approach is that example code presented in the document is
+pulled from the examples directory in the repo which avoids
+a duplicate set of "code" in the document which is likely to
+become out of date.
+
+The documents in the scraped directory (<repo-root>/docs)
+can be created with the command 'make publish' executed
+in the source (src) directory.
--- /dev/null
+# Copyright (c) 2020 Nokia
+# Copyright (c) 2020 AT&T Intellectual Property.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#==================================================================================
+
+publish:
+ (cd user; make publish)
--- /dev/null
+
+This directory is the root of documentation source.
+Below this directory you might find:
+
+ lib - Directory containing common macros for all
+ documents that live here.
+
+ user - User guide document
+
+
+In this directory running the command 'make publish' will
+build and push all documents to the RTD scraper directory.
+Simple documents, such as the release notes, are sourced
+directly at this level as they require only a single
+bit of input.
+
+
--- /dev/null
+.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
+ Mnemonic: front_junk.im
+ Absgtract: This is the front matter (junk):
+ Title page,
+ table of contents
+ license (for page oriented output)
+
+ If defined, this uses the following variables:
+ doc_title
+ doc_subtitle
+ orig_date
+
+ Date: 21 April 2020
+.fi
+
+.if ! _front_junk
+ .gv e OUTPUT_TYPE ot
+ .if "&ot" "generic_ps" =
+ .sp 20
+ .st 24
+ .bc start
+ .if doc_title
+ &doc_title
+ .fi
+ .br
+ .if doc_subtitle
+ &doc_subtitle
+ .fi
+ .bc end
+
+ .st &textsize
+
+ .sp 25
+ .if orig_date
+ Original: &orig_date .br
+ .fi
+ .gv Date
+ Revised: &_date
+ .pa
+
+ .lw 0
+ .pn on noline center roman 0
+ .im &{lib}/license.im
+ .pa
+
+ .gv e XFM_PASS pass
+ .if &pass 1 =
+ .tc on
+ .ei
+ .tc off
+
+ .im &{fname_base}.toc .** initial page eject inside
+ .pa
+ .fi
+
+ .** ensure these happen after toc is rendered
+ .if pfm
+ .pn on 0 noline center f=%d
+ .cd 1 6.0i
+ .fi
+ .ei
+ .if "&ot" "rst" =
+ .dv raw_comment_sym ^..
+ .cd 1 26i
+ .ll 18i
+ .im &{lib}/raw_license.im
+ .ll 6i
+
+ .if doc_title
+ &many_equals
+ &doc_title
+ &many_equals
+ .fi
+ .if doc_subtitle
+ &many_dashes
+ &doc_subtitle
+ &many_dashes
+ .fi
+ .fi
+
+ .dv _front_junk 1
+.fi
+
--- /dev/null
+.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
+
+.** macros compatable with the roff/troff and rts imbed files
+.** this is included when generating postscript from the man source.
+
+ .dv esc : .** rst needs an escape for some things
+
+ .hn off
+ .dv text_size 10p
+ .dv ex_size 8p
+ .dv text_font Helvetica
+ .dv ital_font Helvetica-Oblique
+ .dv bold_font Helvetica-Bold
+ .dv cw_font Courier
+ .st &text_size
+ .sf &text_font
+ .dh 1 f=&bold_font p=12 e=no s=2,1 m=.5i i=0i
+ .dh 2 f=&bold_font p=10 s=1,.5 m=.5i i=0i
+ .dh 3 f=&bold_font p=10 s=1,0 m=.5i i=0i
+
+ .dv comment .** ignore
+
+ .dv h1 .h1 $1
+ .dv h2 .h2 $1
+ .dv h3 .h3 $1
+
+ .dv fig
+ .dv set_font_cw .sf ^&cw_font
+
+ .dv nf .nf
+ .dv fo .fo
+
+ .dv center_start .bc start
+ .dv center_end .bc end
+
+ .dv line_len .ll $1 .dv cn_line_len $1 ^:
+ .dv space .sp 1
+ .dv mult_space .sp $1
+ .dv half_space .sp .5
+
+ .dv beg_list .bl $1
+ .dv end_list .el
+ .dv lic1 n
+ .dv lic2 m
+ .dv lic3 l
+
+ .dv figure .fg x=center $1
+
+ .dv beg_dlist .bd $1 $2
+ .dv end_dlist .ed
+ .dv ditem .cc 2 .sp .5 .di $1 ^:
+ .dv di .cc 2 .sp .5 .di $1 ^:
+ .dv li .cc 2 .sp .5 .li
+ .dv item .cc 2 .li
+
+ .dv proto_start .sp 1 .cc .5i .st 9 .sf Courier-bold .nf
+ .dv proto_end .fo on .sf ^&text_font .st ^&text_size .sp .3
+
+ .dv ex_start .sp .5 .st ^&ex_size .sf ^&cw_font .nf
+ .dv ex_end .fo on .sf ^&text_font .st ^&text_size .sp 1
+
+ .dv fo_ex_start .sp .5 .st ^&ex_size .sf ^&cw_font
+ .dv fo_ex_end .sf ^&text_font .st ^&text_size .sp 1
+
+ .** fonts and font macros
+ .dv ital .tf ^&ital_font ^&text_size $1 ^:
+ .dv bold .tf ^&bold_font ^&text_size $1 ^:
+ .dv cw .tf ^&cw_font ^&text_size $1 ^:
+ .dv set_font_prop .sf ^&text_font
+
+ .cd 1 6.0i
+ .ll 6i
+ .pn off
+ .ju on
+ .in .5i
+ .dv cn_indent .5i
+ .dv cn_line_len 6i
+
+ .dv indent .ll -0.5i .in +0.25i
+ .dv uindent .in -0.25i .ll +0.5i
+
+ .dv super .sm ^[ .sm ^&{ss_num}]
+ .dv ss_num 1
+ .dv note .dv ss_num ^[ ?%.0f ^&ss_num 1 + ] ^: .sm .tf superscript /2 ^&{ss_num} ^:
+ .dv atbot atbot
--- /dev/null
+
+.gv e OUTPUT_TYPE ot
+
+.if "&ot" "generic_ps" =
+ .sp 5
+ The the source used to produce this document, and the source code of the
+ application and/or libraries described herin are coverd by the following
+ license.
+ &mult_space( 1.5 )
+ .ln
+ .nf
+ 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.
+.fo
+ .ln
+.fi
--- /dev/null
+.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
+ Mnemonic: markdown.im
+ Abstract: This is a setup for a very basic generation of markdown from the few
+ macros which are designed for the RMR doc. It is very scaled down from
+ the markdown imbed file supplied with {X}fm.
+
+ Author: E. Scott Danils
+ Date: 26 October 2016
+-------------------------------------------------------------------------------
+.fi
+
+
+.dh 1 m=.5i e=no s=21 i=0 p=16 f=Helvetica-bold
+.dh 2 m=.5i s=21 i=0 p=14 f=Helvetica-bold
+.dh 3 m=.5i s=10 i=0 p=12 f=Helvetica-bold
+
+.ju on
+.hn off
+
+
+.dv esc : .** rst needs an escape for some things
+
+.** these macros are common for prfm/tfm, markdown will override some
+.dv indent .ll -.5i .in +.25i
+.dv uindent .in -.25i .ll +.5i
+.dv smindent .ll -.2i .in +.1i
+.dv smuindent .in -.1i .ll +.2i
+
+.dv def_list .bd $1
+.dv end_dlist .ed
+.dv bd .bd $1
+.dv ed .ed
+
+.dv ex_start .sp .5 .ll -.25i .in +.25i .sf Courier .st ^&extextsize .fo off
+.** ex_end macro calls _must_ be placed in col 0 to cause an exit from no-format mode.
+.dv ex_end .fo on .sf ^&textfont .st ^&textsize .in -.25i .ll +.25i .sp .1
+
+.dv proto_start .sp 1 .cc .5i .st 9 .sf Courier-bold .nf
+.dv proto_end .fo on .sf ^&text_font .st ^&text_size .sp .3
+
+
+.dv h1 .sp .1 .h1 $1
+.dv h2 .sp .1 .h2 $1
+.dv h3 .sp .1 .h3 $1
+.dv di .di $1 ^:
+
+.** superscript number for start_note macro
+.dv ss_num 1
+.dv super .sm ^[ .sm ^&{ss_num}]
+.dv note .dv ss_num ^[ ^&ss_num 1 + ] ^: .sm ^[ .sm ^&{ss_num}]
+.dv atbot atclose
+
+.dv start_note .cn start atclose Times-roman 8p .5i
+.dv end_note .cn end
+.dv bold $1
+.dv cw $1
+.dv set_font_prop
+.dv ital $1
+.dv lic1 *
+.dv lic2 +
+
+.dv figure .fg $1
+
+.dv line_len .ll $1
+.dv space .sp 1
+.dv half_space .sp 1
+.dv mult_space .sp $1
+
+.** -----------------------------------------------
+.** not used, but might be needed if doc expanded
+.** .dv ta .br ^.ta .br |
+.** .dv et .br ^.et .br
+.** .dv cl |
+.** .dv tr | .br ^.tr .br |
+.** .dv table_head $1
+.** .dv empty_cell
+.** -----------------------------------------------
+
+.dv break .sm ^` ^` .br
+.dv br .sm ^` ^` .br
+.ju off
+.dv image .ep ^[ .sm $2] ($3)
+
+.** no concept of a definition list in markdown; no start/end and just make the text bold to emulate
+.dv beg_dlist .sp 1
+.dv bd .sp 1
+.dv end_dlist .sp 1
+.dv ed .sp 1
+.dv di ^&break **$1:**
+.dv ditem ^&break **$1:**
+
+.dv beg_list .bl *
+.dv item .li
+.dv li .li
+.dv end_list .el
+
+.dv center_start
+.dv center_end
+
+.dv line .sp 1 ------
+
+.** leading indention is significant to markdown, so turn it off
+.in 0
+.dv indent
+.dv uindent .sp 2
+.dv smindent
+.dv smuindent .sp 2
+.dv bold **${1}**
+.dv ital _${1}_
+.dv h1 .sp 2 # $1 .br
+.dv h2 .sp 1 ## $1 .br
+.dv h3 .sp 1 ### $1 .br
+
+.dv ex_start .sp .5 .ll -4 .in .5i .sp 2 .fo off
+.dv ex_end .fo on .in 0i .ll +4 .sp 2
--- /dev/null
+
+.if false
+ This is the "raw" license which goes into the document itself (where
+ possible).
+.fi
+
+&raw_comment_sym This work is licensed under a Creative Commons Attribution 4.0 International License.
+.br
+&raw_comment_sym SPDX-License-Identifier: CC-BY-4.0
+.br
+&raw_comment_sym
+.br
+&raw_comment_sym CAUTION: this document is generated from source in doc/src/*
+.br
+&raw_comment_sym To make changes edit the source and recompile the document.
+.br
+&raw_comment_sym Do NOT make changes directly to .rst or ^.md files.
+.sp 2
+
--- /dev/null
+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
+ Mnemonic: troff.im
+ Abstract: This file provides macros allowing {X}fm source to generate
+ troff input from {X}fm source when the doc is passed through
+ tfm. This allows the more easily read {X}fm source to be used
+ to generate man pages, and to be able to generate the man pages
+ in various output formats (txt, rst, etc.).
+
+ Author: E. Scott Daniels
+ Date: 27 January 2019
+
+ Some roff information was gleanded from these pages:
+ http://cmd.inp.nsk.su/old/cmd2/manuals/unix/UNIX_Unleashed/ch08.htm
+ https://www.gnu.org/software/groff/manual/html_node/Basics.html
+.fi
+
+.** assume that we're generating roff output when tfm is used. These macros
+.** convert {X}fm input into troff.
+
+.dv h1 .sp 1 ^&bold($1) .br ^&space
+.dv h2 .sp 1 ^&bold($1) .br ^&space
+.dv h3 .sp 1 ^&bold($1) .br ^&space
+
+.dv fig
+.dv set_font_cw .br ^^.ft CW
+
+.dv nf ^^.nf .br .nf
+.dv fo .fo ^^.fi .br
+
+.dv center .br ^^.ce 1 .br $1 .br
+.dv center_start .br ^^.ce 999
+.dv center_end .br ^.ce 0
+
+.dv esc : .** rst needs an escape for some things
+
+.dv line_len ^^.ll $1
+.dv space .br ^^.sp 1 .br
+.dv half_space .br ^^.sp 1 .br
+.dv mult_space .br ^^.sp $1 .br
+.dv beg_list
+.dv end_list
+.dv beg_dlist .dv udi -$1 ^: ^^.in +$1 .br ^^.sp 1 .br
+.dv end_dlist .br ^^.in &udi .br
+.dv ditem ^^.ti ^&udi .br ^&space .br \fB$1\fR .br
+.dv item ^&space \(bu
+.dv li ^&space \(bu
+.** .dv di ^&ditem
+.dv di ^^.ti ^&udi .br ^&space .br \fB$1\fR .br
+
+.dv figure
+
+.dv ex_start ^^.IP .br ^^.nf .br ^^.ft CW .br .nf
+.dv ex_end .fo on .br ^^.ft P .br ^^.fi .br ^&space
+
+.dv proto_start .sp 1 .cc .5i .st 9 .sf Courier-bold .nf
+.dv proto_end .fo on .sf ^&text_font .st ^&text_size .sp .3
+
+.** fonts and font macros
+.dv ital \fI$1\fR
+.dv bold \fB$1\fR
+.dv cw \f(CW$1\fP
+.dv set_font_prop .br ^^.ft P
+
+.ju off
+.in 0
+
+.dv indent ^^.ll -0.5i &space ^^.in +0.25i
+.dv uindent ^^.in -0.25i &space ^^.ll +0.5i
+
--- /dev/null
+.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
+ Mnemonic: rts.im
+ Abstract: This file provides macros allowing {X}fm source to generate
+ rts input from {X}fm source when the doc is passed through
+ tfm, and to generate postscirpt output when passed through
+ pfm. Simalar to the roff.im macro set that allows the generation
+ of troff input for man pages.
+
+ Author: E. Scott Daniels
+ Date: 7 February 2019
+
+
+ Maybe useful (but doesn't explain why real formatters aren't being used)
+ http://docutils.sourceforge.net/docs/user/rst/quickref.html
+.fi
+
+
+ .** convert {X}fm input into rts.
+ .** post processing is needed to strip the leading space that tfm insists on adding.
+
+ .** bloody rst has no consistant marking character, and each header level must be different and as long as the text.
+ .** and of course they don't generate <hx> tags in the resulting HTML, but <section> tags. WTF?
+ .dv many_equals ============================================================================================
+ .dv many_dashes --------------------------------------------------------------------------------------------
+ .dv many_tildas ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ .dv h1 .sp 1 $1 .br &many_equals .sp 1
+ .dv h2 .sp 1 $1 .br &many_dashes .sp 1
+ .dv h3 .sp 1 $1 .br &many_tildas .sp 1
+
+ .** bloody rst won't allow breaks in a bullet list so we have to allow the column to go wide.
+ .dv cd 1 180i m=0i
+
+ .dv h4 **$1**
+ .** .dv h1 === $1 .br === .sp 1
+ .** .dv h2 === $1 .br === .sp 1
+ .** .dv h3 === $1 .br === .sp 1
+
+ .dv fig
+ .dv set_font_cw
+
+ .dv nf .sp 1 ^:^: .br .ll -2 .in +2
+ .dv fo .in -2 .ll +2 .sp 1
+
+ .dv indent
+ .dv uindent
+
+ .dv lic1 +
+ .dv lic2 -
+ .dv lic3 *
+
+ .in 0i .** bloody rst is indention sensitive like markdown; sheesh
+
+ .dv esc \ : .** bloody need to escape _ and * at the end of a word
+ .dv line_len .ll $1
+ .dv space .sp 1
+ .dv half_space .sp 1
+ .dv mult_space .sp $1
+ .dv beg_list .ll 17i .sp 1 .dv lic $1 ^:
+ .dv end_list .ll 6i .sp 1
+
+ .dv figure .fg $1
+
+ .dv beg_dlist .sp 1 .ll -3
+ .dv end_dlist .br .in 0i .ll +3
+
+ .** for now we allow only a single layer of defitems
+ .dv di .in 0i .br $1 .br .in +3
+ .dv ditem .in 0i .br $1 .br .in +3
+ .** diitem is odd and deprecated
+ .dv diitem .in 0i .br $1 .br .in +3
+ .dv item .br &lic
+ .dv li .br &lic
+
+ .dv ex_start .sp 1 ^:^: .br .ll -2 .in +2 .nf
+ .dv ex_end .fo on .in -2 .ll +2 .sp 1
+
+ .dv proto_start .sp 1 .cc .5i .st 9 .sf Courier-bold .nf
+ .dv proto_end .fo on .sf ^&text_font .st ^&text_size .sp .3
+
+ .dv center .br $1 .br
+ .dv center_start .br
+ .dv center_end .br
+
+ .** fonts and font macros
+ .dv ital *$1*
+ .dv bold **$1**
+ .dv cw $1
+ .dv set_font_prop
+
+ .dv table .sp 1 ^[table not supported in rst output] .if false
+ .dv tab_cell
+ .dv tab_row
+ .dv end_table .fi
+
+ .dv super .sm ^[ .sm ^&{ss_num}]
+ .dv ss_num 1
+ .dv note .dv ss_num ^[ ?%.0f ^&ss_num 1 + ] ^: .sm ^^[^&{ss_num}]
+ .** rst has no concept of a page, so all notes go to the close of the doc
+ .dv atbot atclose
+
+ .ju off
--- /dev/null
+.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
+ 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.
+ Date: 29 July 2019
+.fi
+
+
+.if ! _setup_im
+ .gv e XFM_IM_LIB lib
+ .if ! lib
+ .dv lib ../lib
+ .fi
+
+ .** sane default for everything except rst
+ .dv col_width 6.5i
+ .dv textsize 10
+ .dv textfont Helvetica
+
+ .** imbed output type specific macro file
+ .gv e OUTPUT_TYPE ot
+ .im &{lib}/&{ot}.im
+
+
+ .** set up for an index when using pfm and snare file is defined
+ .if pfm
+ .if index_snare_file
+ .ix space .sp 1 .cc 5 %c .ln ^: .sp 0
+ .ix term .br %s `^` ^`` %d
+ .ix groupb .sp .5 %s .br .ll -.25i .in +.25i
+ .ix groupe .sp .1 .in -.25i .ll +.25i
+ .if ! index_cols
+ .dv index_cols 2
+ .fi
+
+ .dv index_col_width [ 6.5 &index_cols / .25 &index_cols 1 - * - ]
+ .dv index_here .pa .ju off .st &textsize .cd 1 i=1i w=7 ^: .h1 INDEX ^: .pn off .lw 0 .sp 0 .tt ^: .cd &index_cols i=1i w=&{index_col_width}i g=.25i ^: .in -i .ll &{index_col_width}i .ix insert
+ .im &index_snare_file
+ .fi
+ .fi
+
+ .dv _setup_im 1
+
+.fi
--- /dev/null
+.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
+ Mnemonic: txt.im
+ Abstract: To generate raw ASCII we just need to 'disable' the macros which
+ are defined to support things like troff, markdown and rts.
+ For each of those macros, we just map to real {X}fm commandd
+ which do the right thing when generating real .txt output.
+
+ Author: E. Scott Daniels
+ Date: 29 July 2019
+
+.fi
+
+
+.cd 1 6.5i i=0
+.in .25i
+
+.** paragraph headers
+.dv h1 .h1 $1
+.dv h2 .h2 $1
+.dv h3 .h3 $1
+.dv h4 .h4 $1
+
+.dh 1 s=2,1 i=.25i m=.25i
+.dh 2 s=1,1 i=.25i m=.25i
+.dh 3 s=1,0 i=.25i m=.25i
+.hn off
+
+.dv fig .fi t=figure
+.dv set_font_cw
+
+.dv nf .sp 1 ^:^: .br .ll -2 .in +2
+.dv fo .in -2 .ll +2 .sp 1
+
+.dv indent .ll -.25i .in +.25i
+.dv uindent .in -.25i .ll +.25i
+
+.dv lic1 *
+.dv lic2 +
+.dv lic3 -
+
+.dv esc : .** rst needs an escape for some things
+
+.dv line_len .ll $1
+.dv space .sp 1
+.dv mult_space .sp $1
+.dv half_space .sp 1
+.dv beg_list .bl $1
+.dv end_list .el
+.dv li .li
+.dv item .li
+
+.dv beg_dlist .sp 1 .bd $1
+.dv end_dlist .ed
+
+.dv center .ce $1 ^:
+.dv center_start .bc start
+.dv center_end .bc end
+
+.dv figure .fg $1
+
+.** for now we allow only a single layer of defitems
+.dv di .di $1 ^:
+.dv diitem .di $1 ^:
+.dv item .li
+
+.dv ex_start .sp 1 .ll -2 .in +2 .nf
+.dv ex_end .fo on .in -2 .ll +2 .sp 1
+
+.dv proto_start .sp 1 .cc .5i .st 9 .sf Courier-bold .nf
+.dv proto_end .fo on .sf ^&text_font .st ^&text_size .sp .3
+
+.** fonts and font macros
+.dv ital $1
+.dv bold $1
+.dv cw $1
+.dv set_font_prop
+
+.dv table .sp 1 ^[table not supported in plain txt output] .if false
+.dv tab_cell
+.dv tab_row
+.dv end_table .fi
+
+.dv super .sm ^[ .sm ^&{ss_num}]
+.dv ss_num 1
+.dv note .dv ss_num ^[ ?%.0f ^&ss_num 1 + ] ^: .sm ^^[^&{ss_num}]
+.** pure ascii out has no concept of a page, so all notes go to the close of the doc
+.dv atbot atclose
+.ju on
+
--- /dev/null
+*.ps
+*.rst
+*.txt
+*.html
+*.sp
--- /dev/null
+#==================================================================================
+# Copyright (c) 2020 Nokia
+# Copyright (c) 2020 AT&T Intellectual Property.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#==================================================================================
+
+# this uses {X}fm which can be cloned from: https://gitlab.com/rouxware/xfm
+
+docs = user_guide
+src = user_guide.xfm
+imbed_src = cpp_frame.im example1.im example2.im example3.im
+desired_out = rst ps
+
+# 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/^ //' >$@
+
+%.txt: %.xfm
+ OUTPUT_TYPE=txt XFM_PASS=1 tfm $< /dev/null
+ OUTPUT_TYPE=txt XFM_PASS=2 tfm $< $@
+
+# -----------------------------------------------------------------------------------
+all: $(desired_out:%=user_guide.%)
+
+user_guide.ps: user_guide.xfm $(imbed_src)
+
+# we force the .rst docs to always be out of date so that we don't have to
+# jump hoops to ensure that they build for make publish. (See hack rule
+# at the end of the file.)
+#
+$(docs:%=%.rst): always
+
+publish: user_guide.rst
+ cp *.rst ../../../docs/
+
+# intermeidate junk that might straggle
+clean:
+ rm -fr *.bcnfile *.ca *.ecnfile *.sp
+
+# Destroy anything that can be built
+nuke: clean
+ rm -fr *.md *.ps *.pdf *.txt *.rst *.toc
+
+# make hack to force a rule to always be out of date
+always:
+
--- /dev/null
+
+.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
+
+&h1(Introduction)
+The C++ framework allows the programmer to create an xApp object instance, and to use that instance as
+the logic base.
+The xApp object provides a message level interface to the RIC Message Router (RMR), including the ability
+to register callback functions which the instance will drive as messages are received; much in the same
+way that an X-windows application is driven by the window manager for all activity.
+The xApp may also choose to use its own send/receive loop, and thus is not required to use the callback
+driver mechanism provided by the framework.
+
+&h1(The Framework API)
+The C++ framework API consists of the creation of the xApp object, and invoking desired functions via
+the instance of the object.
+The following paragraphs cover the various steps involved to create an xApp instance, wait for a route
+table to arrive, send a message, and wait for messages to arrive.
+
+&h2(Creating the xApp instance)
+The creation of the xApp instance is as simple as invoking the object's constructor with two required
+parameters:
+
+&half_space
+&indent
+&beg_dlist(.5i:&ditemtext)
+ &ditem(port) A C string (char *) which defines the port that RMR will open to listen for connections.
+ &half_space
+
+ &ditem(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 &cw(RMR_SEED_RT) environment variable).
+&end_dlist
+&uindent
+&space
+
+The following code sample illustrates the simplicity of creating the instance of the xApp object.
+&half_space
+
+&ex_start
+ #include <memory>
+ #include <ricxfcpp/xapp.hpp>
+ int main( ) {
+ std::unique_ptr<Xapp> xapp;
+ char* listen_port = (char *) "4560"; //RMR listen port
+ bool wait4table = true; // wait for a route table
+
+ xapp = std::unique_ptr<Xapp>(
+ new Xapp( listen_port, wait4table ) );
+ }
+&ex_end
+&figure( Creating an xAPP instance.)
+&space
+
+From a compilation perspective, the following is the simple compiler invocation string needed to compile
+and link the above program (assuming that the sample code exists in a file called &cw(man_ex1.cpp).
+&half_space
+
+
+&ex_start
+ g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread
+&ex_end
+&space
+
+The above program, while complete and capable of being compiled, does nothing useful.
+When invoked, RMR will be initialized and will begin listening for a route table; blocking the return
+to the main program until one is received.
+When a valid route table arrives, initialization will complete and the program will exit as there is
+no code following the instruction to create the object.
+
+&h1(Listening For Messages)
+The program in the previous example can be extended with just a few lines of code to enable it to receive
+and process messages.
+The application needs to register a callback function for each message type which it desires to process.
+&space
+
+Once registered, each time a message is received the registered callback for the message type will be
+invoked by the framework.
+
+&h2(Callback Signature)
+As with most callback related systems, a callback must have a well known function signature which generally
+passes event related information and a "user" data pointer which was registered with the function.
+The following is the prototype which callback functions must be defined with:
+&half_space
+
+&ex_start
+ void cb_name( Message& m, int mtype, int subid,
+ int payload_len, Msg_component payload,
+ void* usr_data );
+&ex_end
+&figure( Callback function signature)
+&space
+
+The parameters passed to the callback function are as follows:
+&multi_space( .1 )
+
+&indent
+&beg_dlist(1i:&ditemtext)
+ &ditem(m) A reference to the Message that was received.
+ &half_space
+
+ &ditem(mtype) The message type (allows for disambiguation if the callback is registered for multiple message types).
+ &half_space
+
+ &ditem(subid) The subscription ID from the message.
+ &half_space
+
+ &ditem(payload len) The number of bytes which the sender has placed into the payload.
+ &half_space
+
+ &ditem(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.)
+ &half_space
+ &ditem(user data) A pointer to user data. This is the pointer that was provided when the function was registered.
+&end_dlist
+&uindent
+&space
+
+To illustrate the use of a callback function, the previous code example has been extended to add the
+function, register it for message types 1000 and 1001, and to invoke the &cw(Run()) function in the
+framework (explained in the next section).
+
+&ex_start
+ #include <memory>
+ #include <ricxfcpp/xapp.hpp>
+ long m1000_count = 0; // message counters, one for each type
+ long m1001_count = 0;
+
+ // callback function that will increase the appropriate counter
+ void cbf( Message& mbuf, int mtype, int subid, int len,
+ Msg_component payload, void* data ) {
+ long* counter;
+
+ if( (counter = (long *) data) != NULL ) {
+ (*counter)++;
+ }
+ }
+
+ int main( ) {
+ std::unique_ptr<Xapp> xapp;
+ char* listen_port = (char *) "4560";
+ bool wait4table = false;
+
+ xapp = std::unique_ptr<Xapp>(
+ new Xapp( listen_port, wait4table ) );
+
+ // register the same callback function for both msg types
+ xapp->Add_msg_cb( 1000, cbf, (void *) &m1000_count );
+ xapp->Add_msg_cb( 1001, cbf, (void *) &m1001_count );
+
+ xapp->Run( 1 ); // start the callback driver
+ }
+&ex_end
+&figure( Callback function example.)
+&space
+
+As before, the program does nothing useful, but now it will execute and receive messages.
+For this example,
+the same function can be used to increment the appropriate counter simply by providing a pointer to the
+counter as the user data when the callback function is registered.
+In addition, a subtle change from the previous example has been to set the wait for table flag to &cw(false.)
+&space
+
+For an xApp that is a receive only application (never sends) it is not necessary to wait for RMR
+to receive a table from the Route Manager.
+
+&h2(Registering A Default Callback)
+The xApp may also register a default callback function such that the function will be invoked for any
+message that does not have a registered callback.
+If the xAPP does not register a default callback, any message which cannot be mapped to a known callback
+function is silently dropped.
+A default callback is registered by providing a &ital(generic) message type of &cw(xapp->DEFAULT_CALLBACK)
+on an &cw(Add_msg_cb) call.
+
+&h2(The Framework Callback Driver)
+The &cw(Run()) function within the Xapp object is invoked to start the callback driver, and the xApp
+should not expect the function
+to return under most circumstances.
+The only parameter that the &cw(Run()) function expects is the number of threads to start.
+For each thread requested, the framework will start a listener thread which will allow received messages
+to be processed in parallel.
+If supplying a value greater than one, the xApp must ensure that the callback functions are thread safe
+as it is very likely that
+the same callback function will be invoked concurrently from multiple threads.
+
+&h1(Sending Messages)
+It is very likely that most xApps will need to send messages and will not operate in "receive only" mode.
+Sending the message is a function of the message object itself and can take one of two forms:
+
+&half_space
+&indent
+&beg_list &lic1
+ &item Replying to the sender of a received message
+ &half_space
+
+ &item Sending a message (routed based on the message type and subscription ID)
+&end_list
+&uindent
+&space
+
+When replying to the sender, the message type and subscription ID are not used to determine the destination
+of the message; RMR ensures that the message is sent back to the originating xApp. The xApp may still need
+to change the message type and/or the subscription ID in the message prior to using the reply function.
+&space
+
+To provide for both situations, two reply functions are supported by the Message object as illustrated
+with the following prototypes.
+&half_space
+
+&ex_start
+ bool Send_response( int mtype, int subid, int response_len,
+ std:shared_ptr<unsigned char> response );
+
+ bool Send_response( int response_len, std::shared_ptr<unsigned char> response );
+&ex_end
+&figure( Reply function prototypes. )
+&space
+
+In the first prototype the xApp must supply the new message type and subscription ID values, where the
+second function uses the values which are currently set in the message.
+Further, the new payload contents, and length, are supplied to both functions; the framework ensures
+that the message is large enough to accommodate the payload, reallocating it if necessary, and copies
+the response into the message payload prior to sending.
+Should the xApp need to change either the message type, or the subscription ID, but not both, the &cw(NO_CHANGE)
+constant can be used as illustrated below.
+&half_space
+
+&ex_start
+ msg->Send_response( Message::NO_CHANGE, Message::NO_SUBID,
+ pl_length, (unsigned char *) payload );
+&ex_end
+&figure( Send response prototype. )
+&space
+
+In addition to the two function prototypes for &cw(Send_response()) there are two additional prototypes
+which allow the new payload to be supplied as a shared smart pointer.
+The other parameters to these functions are identical to those illustrated above, and thus are not presented
+here.
+&space
+
+The &cw(Send_msg()) set of functions supported by the Message object are identical to the &cw(Send_response())
+functions and are shown below.
+&half_space
+
+&ex_start
+ bool Send_msg( int mtype, int subid, int payload_len,
+ std::shared_ptr<unsigned char> payload );
+
+ bool Send_msg( int mtype, int subid, int payload_len,
+ unsigned char* payload );
+
+ bool Send_msg( int payload_len,
+ std::shared_ptr<unsigned char> payload );
+
+ bool Send_msg( int payload_len, unsigned char* payload );
+&ex_end
+&figure( Send function prototypes. )
+&space
+
+Each send function accepts the message, copies in the payload provided, sets the message type and subscription
+ID (if provided), and then causes the message to be sent.
+The only difference between the &cw(Send_msg()) and &cw(Send_response()) functions is that the destination
+of the message is selected based on the mapping of the message type and subscription ID using the current
+routing table known to RMR.
+
+&h2(Direct Payload Manipulation)
+For some applications, it might be more efficient to manipulate the payload portion of an Xapp Message
+in place, rather than creating it and relying on a buffer copy when the message is finally sent.
+To achieve this, the xApp must either use the smart pointer to the payload passed to the callback function,
+or retrieve one from the message using &cw(Get_payload()) when working with a message outside of a
+callback function.
+Once the smart pointer is obtained, the pointer's get() function can be used to directly reference the
+payload (unsigned char) bytes.
+&space
+
+When working directly with the payload, the xApp must take care not to write more than the actual payload
+size which can be extracted from the Message object using the &cw(Get_available_size()) function.
+&space
+
+When sending a message where the payload has been directly altered, and no extra buffer copy is needed,
+a NULL pointer should be passed to the Message send function.
+The following illustrates how the payload can be directly manipulated and returned to the sender (for
+simplicity, there is no error handling if the payload size of the received message isn't large enough
+for the response string, the response is just not sent).
+&half_space
+
+&ex_start
+ 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 );
+ }
+&ex_end
+&figure( Send message without buffer copy. )
+&space
+
+&h2(Sending Multiple Responses)
+It is likely that the xApp will wish to send multiple responses back to the process that sent a message
+that triggered the callback.
+The callback function may invoke the &cw(Send_response()) function multiple times before returning.
+&space
+
+After each call, the Message retains the necessary information to allow for a subsequent invocation to
+send more data.
+It should be noted though, that after the first call to &cw({Send_response()) the original payload
+will be lost; if necessary, the xApp must make a copy of the payload before the first response call is
+made.
+
+&h2(Message Allocation)
+Not all xApps will be "responders," meaning that some xApps will need to send one or more messages before
+they can expect to receive any messages back.
+To accomplish this, the xApp must first allocate a message buffer, optionally initialising the payload,
+and then using the message's &cw(Send_msg()) function to send a message out.
+The framework's &cw(Alloc_msg()) function can be used to create a Message object with a desired payload
+size.
+
+&h1(Framework Provided Callbacks)
+The framework itself may provide message handling via the driver such that the xApp might not need to
+implement some message processing functionality.
+Initially, the C++ framework will provide a default callback function to handle the RMR based health
+check messages.
+This callback function will assume that if the message was received, and the callback invoked, that all
+is well and will reply with an OK state.
+If the xApp should need to override this simplistic response, all it needs to do is to register its
+own callback function for the health check message type.
--- /dev/null
+.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(RMR Dump xAPP)
+The RMR dump application is an example built on top of the C++ xApp framework to both
+illustrate the use of the framework, and to provide a useful diagnostic tool when
+testing and troubleshooting xApps.
+&space
+
+The RMR dump xApp isn't a traditional xApp inasmuch as its goal is to listen for message
+types and to dump information about the messages received to the TTY much as &cw(tcpdump)
+does for raw packet traffic.
+The full source code, and Makefile, are in the &cw(examples) directory of the C++ framework repo.
+&space
+
+When invoked, the RMR dump program is given one or more message types to listen for.
+A callback function is registered for each, and the framework &cw(Run()) function is invoked
+to drive the process.
+For each recognised message, and depending on the verbosity level supplied at program start,
+information about the received message(s) is written to the TTY.
+If the forwarding option, -f, is given on the command line, and an appropriate route table is
+provided, each received message is forwarded without change.
+This allows for the insertion of the RMR dump program into a flow, however if the ultimate
+receiver of a message needs to reply to that message, the reply will not reach the original
+sender, so RMR dump is not a complete "middle box" application.
+&space
+
+The following is the code for this xAPP. Several functions, which provide logic unrelated to
+the framework, have been omitted. The full code is in the framework repository.
+&half_space
+
+&space
+&indent
+.** pull in the code from the example directory
+&ex_start
+.im j=start-example ../../../examples/rmr_dump.cpp
+&ex_end
+&figure( Simple callback application. )
+&uindent
+
--- /dev/null
+.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(Callback Receiver)
+This sample programme implements a simple message listener which
+registers three callback functions to process two specific
+message types and a default callback to handle unrecognised messages.
+
+&space
+When a message of type 1 is received, it will send two response messages
+back to the sender.
+Two messages are sent in order to illustrate that it is possible to
+send multiple responses using the same received message.
+
+&space
+The programme illustrates how multiple listening threads can be
+used, but the programme is &bold(not) thread safe; to keep
+this example as simple as possible, the counters
+are not locked when incremented.
+
+&space
+&indent
+.** pull in the code from the example directory
+&ex_start
+.im j=start-example ../../../examples/xapp_t1.cpp
+&ex_end
+&figure( Simple callback application. )
+&uindent
+&space
--- /dev/null
+.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(Looping Sender)
+This is another very simple application which demonstrates how an
+application can control its own listen loop while sending messages.
+As with the other examples, some error checking is skipped, and short cuts have
+been made in order to keep the example small and to the point.
+
+
+&space
+&indent
+.** pull in the code from the example directory
+&ex_start
+.im j=start-example ../../../examples/xapp_t2.cpp
+&ex_end
+&figure( Simple looping sender application. )
+&space
+
--- /dev/null
+.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
+ Mnemonic: user_guide.xfm
+ Abstract: Source for the user guide. Use {X}fm to generate one of several
+ output types:
+ - postscript
+ - roff
+ - markdown
+ - rst
+ - ascii text
+
+ Date: 21 April 2020
+ Author: E. Scott Daniels
+.fi
+
+
+.dv col_width 6.5i
+.dv fname_base user_guide
+.dv doc_title RIC xAPP C++ Framework
+.dv doc_subtitle User's Guide
+.dv orig_date 21 April 2020
+
+.dv textfont Helvetica
+.dv ditemtext Helvetica-bold
+
+.in .5i
+.ll 6i
+.hn off
+
+.im ../lib/setup.im
+
+.im &{lib}/front_junk.im
+
+.im cpp_frame.im
+
+&h1(Example Programmes)
+The following sections contain several example programmes which are written on
+top of the C++ framework.
+
+.im example1.im
+.im example2.im
+.im example3.im
--- /dev/null
+/_build
+/out
--- /dev/null
+from docs_conf.conf import *
+linkcheck_ignore = [
+ 'http://localhost.*',
+ 'http://127.0.0.1.*',
+ 'https://gerrit.o-ran-sc.org.*'
+]
--- /dev/null
+---
+project_cfg: oran
+project: portal-ric-xappframe-cpp
--- /dev/null
+
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. SPDX-License-Identifier: CC-BY-4.0
+
+
+.. who in bloody hell knows what format this should be; guessing!
+.. it does seem though that every .rst file in the directory must be listed here
+.. or tox will complain and fail.
+
+
+RIC xAPP C++ Framework
+======================
+
+.. toctree::
+ :maxdepth: 1
+ :caption: Documents:
+
+ user-guide.rst
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Misc:
+
+ rel-notes.rst
+
+* :ref:`search`
--- /dev/null
+
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. SPDX-License-Identifier: CC-BY-4.0
+
+.. CAUTION: this document is generated from source in doc/src/rtd.
+.. To make changes edit the source and recompile the document.
+.. Do NOT make changes directly to .rst or .md files.
+
+RIC xAPP C++ Framework
+======================
+
+2020 April 22 Version 1.0.0
+ Initial release
+
--- /dev/null
+sphinx
+sphinx-rtd-theme
+sphinxcontrib-httpdomain
+recommonmark
+lfdocs-conf
--- /dev/null
+
+
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. SPDX-License-Identifier: CC-BY-4.0
+..
+.. CAUTION: this document is generated from source in doc/src/*
+.. To make changes edit the source and recompile the document.
+.. Do NOT make changes directly to .rst or .md files.
+
+
+============================================================================================
+RIC xAPP C++ Framework
+============================================================================================
+--------------------------------------------------------------------------------------------
+User's Guide
+--------------------------------------------------------------------------------------------
+
+Introduction
+============================================================================================
+
+The C++ framework allows the programmer to create an xApp
+object instance, and to use that instance as the logic base.
+The xApp object provides a message level interface to the RIC
+Message Router (RMR), including the ability to register
+callback functions which the instance will drive as messages
+are received; much in the same way that an X-windows
+application is driven by the window manager for all activity.
+The xApp may also choose to use its own send/receive loop,
+and thus is not required to use the callback driver mechanism
+provided by the framework.
+
+The Framework API
+============================================================================================
+
+The C++ framework API consists of the creation of the xApp
+object, and invoking desired functions via the instance of
+the object. The following paragraphs cover the various steps
+involved to create an xApp instance, wait for a route table
+to arrive, send a message, and wait for messages to arrive.
+
+Creating the xApp instance
+--------------------------------------------------------------------------------------------
+
+The creation of the xApp instance is as simple as invoking
+the object's constructor with two required parameters:
+
+
+
+ port
+
+ A C string (char *) which defines the port that RMR will
+ open to listen for connections.
+
+
+ wait
+
+ A Boolean value which indicates whether or not the
+ initialization process should wait for the arrival of a
+ valid route table before completing. When true is
+ supplied, the initialization will not complete until RMR
+ has received a valid route table (or one is located via
+ the RMR_SEED_RT environment variable).
+
+
+ The following code sample illustrates the simplicity of
+ creating the instance of the xApp object.
+
+
+ ::
+
+ #include <memory>
+ #include <ricxfcpp/xapp.hpp>
+ int main( ) {
+ std::unique_ptr<Xapp> xapp;
+ char* listen_port = (char *) "4560"; //RMR listen port
+ bool wait4table = true; // wait for a route table
+ xapp = std::unique_ptr<Xapp>(
+ new Xapp( listen_port, wait4table ) );
+ }
+
+
+ Figure 1: Creating an xAPP instance.
+
+ From a compilation perspective, the following is the simple
+ compiler invocation string needed to compile and link the
+ above program (assuming that the sample code exists in a file
+ called man_ex1.cpp.
+
+
+ ::
+
+ g++ man_ex1.cpp -o man_ex1 -lricxfcpp -lrmr_si -lpthread
+
+
+
+ The above program, while complete and capable of being
+ compiled, does nothing useful. When invoked, RMR will be
+ initialized and will begin listening for a route table;
+ blocking the return to the main program until one is
+ received. When a valid route table arrives, initialization
+ will complete and the program will exit as there is no code
+ following the instruction to create the object.
+
+ Listening For Messages
+ ============================================================================================
+
+ The program in the previous example can be extended with just
+ a few lines of code to enable it to receive and process
+ messages. The application needs to register a callback
+ function for each message type which it desires to process.
+
+ Once registered, each time a message is received the
+ registered callback for the message type will be invoked by
+ the framework.
+
+ Callback Signature
+ --------------------------------------------------------------------------------------------
+
+ As with most callback related systems, a callback must have a
+ well known function signature which generally passes event
+ related information and a "user" data pointer which was
+ registered with the function. The following is the prototype
+ which callback functions must be defined with:
+
+
+ ::
+
+ void cb_name( Message& m, int mtype, int subid,
+ int payload_len, Msg_component payload,
+ void* usr_data );
+
+
+ Figure 2: Callback function signature
+
+ The parameters passed to the callback function are as
+ follows: &multi_space
+
+
+ m
+
+ A reference to the Message that was received.
+
+
+ mtype
+
+ The message type (allows for disambiguation if the
+ callback is registered for multiple message types).
+
+
+ subid
+
+ The subscription ID from the message.
+
+
+ payload len
+
+ The number of bytes which the sender has placed into the
+ payload.
+
+
+ payload
+
+ A direct reference (smart pointer) to the payload. (The
+ smart pointer is wrapped in a special class in order to
+ provide a custom destruction function without burdening
+ the xApp developer with that knowledge.)
+
+
+ user data
+
+ A pointer to user data. This is the pointer that was
+ provided when the function was registered.
+
+
+ To illustrate the use of a callback function, the previous
+ code example has been extended to add the function, register
+ it for message types 1000 and 1001, and to invoke the Run()
+ function in the framework (explained in the next section).
+
+ ::
+
+ #include <memory>
+ #include <ricxfcpp/xapp.hpp>
+ long m1000_count = 0; // message counters, one for each type
+ long m1001_count = 0;
+ // callback function that will increase the appropriate counter
+ void cbf( Message& mbuf, int mtype, int subid, int len,
+ Msg_component payload, void* data ) {
+ long* counter;
+ if( (counter = (long *) data) != NULL ) {
+ (*counter)++;
+ }
+ }
+ int main( ) {
+ std::unique_ptr<Xapp> xapp;
+ char* listen_port = (char *) "4560";
+ bool wait4table = false;
+ xapp = std::unique_ptr<Xapp>(
+ new Xapp( listen_port, wait4table ) );
+ // register the same callback function for both msg types
+ xapp->Add_msg_cb( 1000, cbf, (void *) &m1000_count );
+ xapp->Add_msg_cb( 1001, cbf, (void *) &m1001_count );
+ xapp->Run( 1 ); // start the callback driver
+ }
+
+
+ Figure 3: Callback function example.
+
+ As before, the program does nothing useful, but now it will
+ execute and receive messages. For this example, the same
+ function can be used to increment the appropriate counter
+ simply by providing a pointer to the counter as the user data
+ when the callback function is registered. In addition, a
+ subtle change from the previous example has been to set the
+ wait for table flag to false.
+
+ For an xApp that is a receive only application (never sends)
+ it is not necessary to wait for RMR to receive a table from
+ the Route Manager.
+
+ Registering A Default Callback
+ --------------------------------------------------------------------------------------------
+
+ The xApp may also register a default callback function such
+ that the function will be invoked for any message that does
+ not have a registered callback. If the xAPP does not register
+ a default callback, any message which cannot be mapped to a
+ known callback function is silently dropped. A default
+ callback is registered by providing a *generic* message type
+ of xapp->DEFAULT_CALLBACK on an Add_msg_cb call.
+
+ The Framework Callback Driver
+ --------------------------------------------------------------------------------------------
+
+ The Run() function within the Xapp object is invoked to start
+ the callback driver, and the xApp should not expect the
+ function to return under most circumstances. The only
+ parameter that the Run() function expects is the number of
+ threads to start. For each thread requested, the framework
+ will start a listener thread which will allow received
+ messages to be processed in parallel. If supplying a value
+ greater than one, the xApp must ensure that the callback
+ functions are thread safe as it is very likely that the same
+ callback function will be invoked concurrently from multiple
+ threads.
+
+ Sending Messages
+ ============================================================================================
+
+ It is very likely that most xApps will need to send messages
+ and will not operate in "receive only" mode. Sending the
+ message is a function of the message object itself and can
+ take one of two forms:
+
+
+ +
+ $1 Replying to the sender of a received message
+
+ $1 Sending a message (routed based on the message type and subscription ID)
+
+
+ When replying to the sender, the message type and
+ subscription ID are not used to determine the destination of
+ the message; RMR ensures that the message is sent back to the
+ originating xApp. The xApp may still need to change the
+ message type and/or the subscription ID in the message prior
+ to using the reply function.
+
+ To provide for both situations, two reply functions are
+ supported by the Message object as illustrated with the
+ following prototypes.
+
+
+ ::
+
+ bool Send_response( int mtype, int subid, int response_len,
+ std:shared_ptr<unsigned char> response );
+ bool Send_response( int response_len, std::shared_ptr<unsigned char> response );
+
+
+ Figure 4: Reply function prototypes.
+
+ In the first prototype the xApp must supply the new message
+ type and subscription ID values, where the second function
+ uses the values which are currently set in the message.
+ Further, the new payload contents, and length, are supplied
+ to both functions; the framework ensures that the message is
+ large enough to accommodate the payload, reallocating it if
+ necessary, and copies the response into the message payload
+ prior to sending. Should the xApp need to change either the
+ message type, or the subscription ID, but not both, the
+ NO_CHANGE constant can be used as illustrated below.
+
+
+ ::
+
+ msg->Send_response( Message::NO_CHANGE, Message::NO_SUBID,
+ pl_length, (unsigned char *) payload );
+
+
+ Figure 5: Send response prototype.
+
+ In addition to the two function prototypes for
+ Send_response() there are two additional prototypes which
+ allow the new payload to be supplied as a shared smart
+ pointer. The other parameters to these functions are
+ identical to those illustrated above, and thus are not
+ presented here.
+
+ The Send_msg() set of functions supported by the Message
+ object are identical to the Send_response() functions and are
+ shown below.
+
+
+ ::
+
+ bool Send_msg( int mtype, int subid, int payload_len,
+ std::shared_ptr<unsigned char> payload );
+ bool Send_msg( int mtype, int subid, int payload_len,
+ unsigned char* payload );
+ bool Send_msg( int payload_len,
+ std::shared_ptr<unsigned char> payload );
+ bool Send_msg( int payload_len, unsigned char* payload );
+
+
+ Figure 6: Send function prototypes.
+
+ Each send function accepts the message, copies in the payload
+ provided, sets the message type and subscription ID (if
+ provided), and then causes the message to be sent. The only
+ difference between the Send_msg() and Send_response()
+ functions is that the destination of the message is selected
+ based on the mapping of the message type and subscription ID
+ using the current routing table known to RMR.
+
+ Direct Payload Manipulation
+ --------------------------------------------------------------------------------------------
+
+ For some applications, it might be more efficient to
+ manipulate the payload portion of an Xapp Message in place,
+ rather than creating it and relying on a buffer copy when the
+ message is finally sent. To achieve this, the xApp must
+ either use the smart pointer to the payload passed to the
+ callback function, or retrieve one from the message using
+ Get_payload() when working with a message outside of a
+ callback function. Once the smart pointer is obtained, the
+ pointer's get() function can be used to directly reference
+ the payload (unsigned char) bytes.
+
+ When working directly with the payload, the xApp must take
+ care not to write more than the actual payload size which can
+ be extracted from the Message object using the
+ Get_available_size() function.
+
+ When sending a message where the payload has been directly
+ altered, and no extra buffer copy is needed, a NULL pointer
+ should be passed to the Message send function. The following
+ illustrates how the payload can be directly manipulated and
+ returned to the sender (for simplicity, there is no error
+ handling if the payload size of the received message isn't
+ large enough for the response string, the response is just
+ not sent).
+
+
+ ::
+
+ Msg_component payload; // smart reference
+ int pl_size; // max size of payload
+ payload = msg->Get_payload();
+ pl_size = msg->Get_available_size();
+ if( snprintf( (char *) payload.get(), pl_size,
+ "Msg Received\\n" ) < pl_size ) {
+ msg->Send_response( M_TYPE, SID, strlen( raw_pl ), NULL );
+ }
+
+
+ Figure 7: Send message without buffer copy.
+
+
+ Sending Multiple Responses
+ --------------------------------------------------------------------------------------------
+
+ It is likely that the xApp will wish to send multiple
+ responses back to the process that sent a message that
+ triggered the callback. The callback function may invoke the
+ Send_response() function multiple times before returning.
+
+ After each call, the Message retains the necessary
+ information to allow for a subsequent invocation to send more
+ data. It should be noted though, that after the first call to
+ {Send_response() the original payload will be lost; if
+ necessary, the xApp must make a copy of the payload before
+ the first response call is made.
+
+ Message Allocation
+ --------------------------------------------------------------------------------------------
+
+ Not all xApps will be "responders," meaning that some xApps
+ will need to send one or more messages before they can expect
+ to receive any messages back. To accomplish this, the xApp
+ must first allocate a message buffer, optionally initialising
+ the payload, and then using the message's Send_msg() function
+ to send a message out. The framework's Alloc_msg() function
+ can be used to create a Message object with a desired payload
+ size.
+
+ Framework Provided Callbacks
+ ============================================================================================
+
+ The framework itself may provide message handling via the
+ driver such that the xApp might not need to implement some
+ message processing functionality. Initially, the C++
+ framework will provide a default callback function to handle
+ the RMR based health check messages. This callback function
+ will assume that if the message was received, and the
+ callback invoked, that all is well and will reply with an OK
+ state. If the xApp should need to override this simplistic
+ response, all it needs to do is to register its own callback
+ function for the health check message type.
+
+ Example Programmes
+ ============================================================================================
+
+ The following sections contain several example programmes
+ which are written on top of the C++ framework.
+
+ RMR Dump xAPP
+ --------------------------------------------------------------------------------------------
+
+ The RMR dump application is an example built on top of the
+ C++ xApp framework to both illustrate the use of the
+ framework, and to provide a useful diagnostic tool when
+ testing and troubleshooting xApps.
+
+ The RMR dump xApp isn't a traditional xApp inasmuch as its
+ goal is to listen for message types and to dump information
+ about the messages received to the TTY much as tcpdump does
+ for raw packet traffic. The full source code, and Makefile,
+ are in the examples directory of the C++ framework repo.
+
+ When invoked, the RMR dump program is given one or more
+ message types to listen for. A callback function is
+ registered for each, and the framework Run() function is
+ invoked to drive the process. For each recognised message,
+ and depending on the verbosity level supplied at program
+ start, information about the received message(s) is written
+ to the TTY. If the forwarding option, -f, is given on the
+ command line, and an appropriate route table is provided,
+ each received message is forwarded without change. This
+ allows for the insertion of the RMR dump program into a flow,
+ however if the ultimate receiver of a message needs to reply
+ to that message, the reply will not reach the original
+ sender, so RMR dump is not a complete "middle box"
+ application.
+
+ The following is the code for this xAPP. Several functions,
+ which provide logic unrelated to the framework, have been
+ omitted. The full code is in the framework repository.
+
+
+
+ ::
+
+ #include <stdio.h>
+ #include <unistd.h>
+ #include <atomic>
+ #include "ricxfcpp/xapp.hpp"
+ /*
+ Information that the callback needs outside
+ of what is given to it via parms on a call
+ 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<long> pcount; // messages processed
+ std::atomic<long> icount; // messages ignored
+ std::atomic<int> 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, "<RD> 0000 | " );
+ j = 0;
+ for( i = 0; i < len; i++ ) {
+ cheater[j++] = isprint( buf[i] ) ? buf[i] : '.';
+ fprintf( stdout, "%02x ", buf[i] );
+ if( j == 16 ) {
+ cheater[j] = 0;
+ fprintf( stdout, " | %s\\n<RD> %04x | ", cheater, i+1 );
+ j = 0;
+ }
+ }
+ if( j ) {
+ i = 16 - (i % 16);
+ for( ; i > 0; i-- ) {
+ fprintf( stdout, " " );
+ }
+ 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" );
+ }
+ cbi.hdr = cbi.stats_freq; // reset must be last
+ }
+ }
+ void cb1( Message& mbuf, int mtype, int subid, int len,
+ Msg_component payload, void* data ) {
+ cb_info_t* cbi;
+ long total_count;
+ if( (cbi = (cb_info_t *) data) == NULL ) {
+ return;
+ }
+ cbi->pcount++;
+ stats( *cbi ); // gen stats & header if needed
+ if( cbi->vlevel > 0 ) {
+ fprintf( stdout, "<RD> %-5d %-5d %02d %-5d \\n",
+ mtype, subid, mbuf.Get_state(), len );
+ if( cbi->vlevel > 1 ) {
+ dump( payload.get(), len > 64 ? 64 : len );
+ }
+ }
+ if( cbi->forward ) {
+ // forward with no change to len or payload
+ mbuf.Send_msg( Message::NO_CHANGE, NULL );
+ }
+ }
+ /*
+ registered as the default callback; it counts the
+ messages that we aren't giving details about.
+ */
+ void cbd( Message& mbuf, int mtype, int subid, int len,
+ Msg_component payload, void* data ) {
+ cb_info_t* cbi;
+ if( (cbi = (cb_info_t *) data) == NULL ) {
+ return;
+ }
+ cbi->icount++;
+ stats( *cbi );
+ if( cbi->forward ) {
+ // forward with no change to len or payload
+ mbuf.Send_msg( Message::NO_CHANGE, NULL );
+ }
+ }
+ int main( int argc, char** argv ) {
+ std::unique_ptr<Xapp> x;
+ char* port = (char *) "4560";
+ int 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, "<RD> listening on port: %s\\n", port );
+ // create xapp, wait for route table if forwarding
+ x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
+ // register callback for each type on the command line
+ while( ai < argc ) {
+ mtype = atoi( argv[ai] );
+ ai++;
+ fprintf( stderr, "<RD> capturing messages for type %d\\n", mtype );
+ x->Add_msg_cb( mtype, cb1, cbi );
+ ncb++;
+ }
+ if( ncb < 1 ) {
+ fprintf( stderr, "<RD> no message types specified on the command line\\n" );
+ exit( 1 );
+ }
+ x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi ); // register default cb
+ fprintf( stderr, "<RD> starting driver\\n" );
+ x->Run( nthreads );
+ // return from run() is not expected, but some compilers might
+ // compilain if there isn't a return value here.
+ return 0;
+ }
+
+
+ Figure 8: Simple callback application.
+
+ Callback Receiver
+ --------------------------------------------------------------------------------------------
+
+ This sample programme implements a simple message listener
+ which registers three callback functions to process two
+ specific message types and a default callback to handle
+ unrecognised messages.
+
+ When a message of type 1 is received, it will send two
+ response messages back to the sender. Two messages are sent
+ in order to illustrate that it is possible to send multiple
+ responses using the same received message.
+
+ The programme illustrates how multiple listening threads can
+ be used, but the programme is **not** thread safe; to keep
+ this example as simple as possible, the counters are not
+ locked when incremented.
+
+
+ ::
+
+ #include <stdio.h>
+ #include "ricxfcpp/message.hpp"
+ #include "ricxfcpp/msg_component.hpp"
+ #include "ricxfcpp/xapp.hpp"
+ // counts; not thread safe
+ long cb1_count = 0;
+ long cb2_count = 0;
+ long cbd_count = 0;
+ long cb1_lastts = 0;
+ long cb1_lastc = 0;
+ // respond with 2 messages for each type 1 received
+ void cb1( Message& mbuf, int mtype, int subid, int len,
+ Msg_component payload, void* data ) {
+ long now;
+ long total_count;
+ // illustrate that we can use the same buffer for 2 rts calls
+ mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK1\\n" );
+ mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\\n" );
+ cb1_count++;
+ }
+ // just count messages
+ void cb2( Message& mbuf, int mtype, int subid, int len,
+ 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;
+ }
+ switch( argv[ai][1] ) { // we only support -x so -xy must be -x -y
+ case 'p':
+ port = argv[ai+1];
+ ai++;
+ break;
+ case 't':
+ nthreads = atoi( argv[ai+1] );
+ ai++;
+ break;
+ }
+ ai++;
+ }
+ fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
+ fprintf( stderr, "<XAPP> starting %d threads\\n", nthreads );
+ x = new Xapp( port, true );
+ x->Add_msg_cb( 1, cb1, NULL ); // register callbacks
+ x->Add_msg_cb( 2, cb2, NULL );
+ x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL );
+ x->Run( nthreads ); // let framework drive
+ // control should not return
+ }
+
+
+ Figure 9: Simple callback application.
+
+
+ Looping Sender
+ --------------------------------------------------------------------------------------------
+
+ This is another very simple application which demonstrates
+ how an application can control its own listen loop while
+ sending messages. As with the other examples, some error
+ checking is skipped, and short cuts have been made in order
+ to keep the example small and to the point.
+
+
+ ::
+
+ #include <stdio.h>
+ #include <string.h>
+ #include <unistd.h>
+ #include <iostream>
+ #include <memory>
+ #include "ricxfcpp/xapp.hpp"
+ extern int main( int argc, char** argv ) {
+ std::unique_ptr<Xapp> xfw;
+ std::unique_ptr<Message> msg;
+ Msg_component payload; // special type of unique pointer to the payload
+ int sz;
+ int len;
+ int i;
+ int 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++;
+ }
+ fprintf( stderr, "<XAPP> response timeout set to: %d\\n", response_to );
+ fprintf( stderr, "<XAPP> listening on port: %s\\n", port );
+ // get an instance and wait for a route table to be loaded
+ xfw = std::unique_ptr<Xapp>( new Xapp( port, true ) );
+ msg = xfw->Alloc_msg( 2048 );
+ for( i = 0; i < 100; i++ ) {
+ mtype++;
+ if( mtype > 10 ) {
+ mtype = 0;
+ }
+ // we'll reuse a received message; get max size
+ sz = msg->Get_available_size();
+ // direct access to payload; add something silly
+ payload = msg->Get_payload();
+ len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i );
+ // payload updated in place, prevent copy by passing nil
+ if ( ! msg->Send_msg( mtype, Message::NO_SUBID, len, NULL )) {
+ fprintf( stderr, "<SNDR> send failed: %d\\n", i );
+ }
+ // receive anything that might come back
+ msg = xfw->Receive( response_to );
+ if( msg != NULL ) {
+ 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 10: Simple looping sender application.
+
on by the application. The verbosity level may be used to increase
the amount of detail given for the tracked messages.
- This writes to the TTY which is slow, so do not expect it to be able
+ This writes to the TTY which is slow, so do not expect it to be able
to process and report on a high rate of messages. Also, forwarded
- messages will reach the intended target, however if the target
+ messages will reach the intended target, however if the target
attempts to send a response the response will come back to THIS
application, and not the message origination; this cannot be a bit
of middleware in it's current form.
Date: 25 March 2020
Author: E. Scott Daniels
+ Caution: This example code is pulled directly into one or more of the documents
+ (starting from the "start-example" tag below. Use caution with
+ line lengths and contents because of the requirement that this
+ be documentation as well as working code.
*/
+// start-example
#include <stdio.h>
#include <unistd.h>
#include <atomic>
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<long> pcount; // messages processed
- std::atomic<long> icount; // messages ignored
- std::atomic<int> hdr; // number of messages before next header
+ int vlevel; // verbosity level
+ bool forward; // if true, message is forwarded
+ int stats_freq; // header/stats after n messages
+ std::atomic<long> pcount; // messages processed
+ std::atomic<long> icount; // messages ignored
+ std::atomic<int> hdr; // number of messages before next header
} cb_info_t;
-// ----------------------------------------------------------
+// ----------------------------------------------------------------------
/*
Dump bytes to tty.
for( i = 0; i < len; i++ ) {
cheater[j++] = isprint( buf[i] ) ? buf[i] : '.';
fprintf( stdout, "%02x ", buf[i] );
-
+
if( j == 16 ) {
cheater[j] = 0;
- fprintf( stdout, " | %s\n<RD> %04x | ", cheater, i+1 );
+ fprintf( stdout, " | %s\n<RD> %04x | ", cheater, i+1 );
j = 0;
}
}
fprintf( stdout, " " );
}
cheater[j] = 0;
- fprintf( stdout, " | %s\n", cheater );
+ fprintf( stdout, " | %s\n", cheater );
}
}
curv = cbi.hdr--;
if( curv == 0 ) { // stats when we reach 0
- fprintf( stdout, "ignored: %ld processed: %ld\n",
+ 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",
+ fprintf( stdout, "\n %5s %5s %2s %5s\n",
"MTYPE", "SUBID", "ST", "PLLEN" );
}
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 in number of messages received\n" );
- fprintf( stderr, "\tverbose levels (-V) 0 counts only, 1 message info 2 payload dump\n" );
+ 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 );
}
x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
// register callback for each type on the command line
- while( ai < argc ) {
+ while( ai < argc ) {
mtype = atoi( argv[ai] );
ai++;
fprintf( stderr, "<RD> capturing messages for type %d\n", mtype );
Abstract: This is a simple demo xapp which registers 3 callback functions
(2 for specific message types, and one default callback). It
defaults to starting 1 thread, but can cause the xapp environment
- to start n listeners. When running 1 thread it should emit
- message countes ever few seconds with a crudly computed msg/sec
+ to start n listeners. When running 1 thread it should emit
+ message countes ever few seconds with a crudly computed msg/sec
receive rate value.
In addition, the callback for message type 1 will send two response
Date: 18 March 2020
Author: E. Scott Daniels
-
+
+ Caution: This example code is pulled directly into one or more of the documents
+ (starting from the "start-example" tag below. Use caution with
+ line lengths and contents because of the requirement that this
+ be documentation as well as working code.
*/
+// start-example
#include <stdio.h>
#include "ricxfcpp/message.hpp"
#include "ricxfcpp/msg_component.hpp"
#include "ricxfcpp/xapp.hpp"
-// ----------------------------------------------------------
-
-// counts/time values for crude rate estimation; only valid when threads == 1
+// counts; not thread safe
long cb1_count = 0;
long cb2_count = 0;
long cbd_count = 0;
long cb1_lastts = 0;
long cb1_lastc = 0;
-void cb1( Message& mbuf, int mtype, int subid, int len, Msg_component payload, void* data ) {
+// 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;
- //fprintf( stderr, "callback 1 got a message type = %d len = %d\n", mtype, len );
- mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK1\n" ); // validate that we can use the same buffer for 2 rts calls
+ // 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++;
-
- now = time( NULL );
- if( now - cb1_lastts > 5 ) { // crude rate estimation starting with second timer pop
- if( cb1_lastts ) {
- total_count = cb1_count + cb2_count;
- fprintf( stderr, "cb1: %ld diff=%ld ~rate=%ld\n", total_count, now - cb1_lastts, (total_count-cb1_lastc)/(now - cb1_lastts) );
- cb1_lastc = total_count;
- }
- cb1_lastts = now;
- }
}
-void cb2( Message& mbuf, int mtype, int subid, int len, Msg_component payload, void* data ) {
- //fprintf( stderr, "callback 2 got a message type = %d len = %d\n", mtype, len );
- //mbuf.Send_msg( 101, -1, 4, (unsigned char *) "HI\n" ); // send, including the trailing 0
+// just count messages
+void cb2( Message& mbuf, int mtype, int subid, int len,
+ Msg_component payload, void* data ) {
cb2_count++;
}
-void cbd( Message& mbuf, int mtype, int subid, int len, Msg_component payload, void* data ) {
- //fprintf( stderr, "default callback got a message type = %d len = %d\n", mtype, len );
+// default to count all unrecognised messages
+void cbd( Message& mbuf, int mtype, int subid, int len,
+ Msg_component payload, void* data ) {
cbd_count++;
}
int ai = 1; // arg processing index
int nthreads = 1;
- ai = 1;
- while( ai < argc ) { // very simple flag processing (no bounds/error checking)
+ // 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];
+ case 'p':
+ port = argv[ai+1];
ai++;
break;
ai++;
}
-
+
fprintf( stderr, "<XAPP> listening on port: %s\n", port );
fprintf( stderr, "<XAPP> starting %d threads\n", nthreads );
x = new Xapp( port, true );
- x->Add_msg_cb( 1, cb1, NULL );
+ x->Add_msg_cb( 1, cb1, NULL ); // register callbacks
x->Add_msg_cb( 2, cb2, NULL );
x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL );
- x->Run( nthreads );
+ x->Run( nthreads ); // let framework drive
+ // control should not return
}
Date: 18 March 2020
Author: E. Scott Daniels
-
+
+ Caution: This example code is pulled directly into one or more of the documents
+ (starting from the "start-example" tag below. Use caution with
+ line lengths and contents because of the requirement that this
+ be documentation as well as working code.
*/
+// start-example
#include <stdio.h>
#include <string.h>
#include "ricxfcpp/xapp.hpp"
-// ----------------------------------------------------------
-
extern int main( int argc, char** argv ) {
std::unique_ptr<Xapp> xfw;
std::unique_ptr<Message> msg;
- Msg_component payload; // special type of unique pointer to the payload
+ 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
int mtype = 0;
int rmtype; // received message type
int delay = 1000000; // mu-sec delay; default 1s
-
- ai = 1;
- while( ai < argc ) { // very simple flag processing (no bounds/error checking)
+
+ // 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 'd': // delay between messages (mu-sec)
+ // 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];
+
+ case 'p':
+ port = argv[ai+1];
ai++;
break;
- case 't': // timeout in seconds; we need to convert to ms for rmr calls
+ // timeout in seconds; we need to convert to ms for rmr calls
+ case 't':
response_to = atoi( argv[ai+1] ) * 1000;
ai++;
break;
fprintf( stderr, "<XAPP> response timeout set to: %d\n", response_to );
fprintf( stderr, "<XAPP> listening on port: %s\n", port );
- xfw = std::unique_ptr<Xapp>( new Xapp( port, true ) ); // new xAPP thing; wait for a route table
+ // get an instance and wait for a route table to be loaded
+ xfw = std::unique_ptr<Xapp>( new Xapp( port, true ) );
msg = xfw->Alloc_msg( 2048 );
for( i = 0; i < 100; i++ ) {
mtype = 0;
}
- sz = msg->Get_available_size(); // we'll reuse a message if we received one back; ensure it's big enough
- if( sz < 2048 ) {
- fprintf( stderr, "<SNDR> fail: message returned did not have enough size: %d [%d]\n", sz, i );
- exit( 1 );
- }
+ // we'll reuse a received message; get max size
+ sz = msg->Get_available_size();
- payload = msg->Get_payload(); // direct access to payload
- snprintf( (char *) payload.get(), 2048, "This is message %d\n", i ); // something silly to send
+ // 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, nothing to copy from, so payload parm is nil
- if ( ! msg->Send_msg( mtype, Message::NO_SUBID, strlen( (char *) payload.get() )+1, NULL )) {
+ // payload updated in place, prevent copy by passing nil
+ if ( ! msg->Send_msg( mtype, Message::NO_SUBID, len, NULL )) {
fprintf( stderr, "<SNDR> send failed: %d\n", i );
}
+ // receive anything that might come back
msg = xfw->Receive( response_to );
if( msg != NULL ) {
rmtype = msg->Get_mtype();
payload = msg->Get_payload();
- fprintf( stderr, "got: mtype=%d payload=(%s)\n", rmtype, (char *) payload.get() );
+ fprintf( stderr, "got: mtype=%d payload=(%s)\n",
+ rmtype, (char *) payload.get() );
} else {
- msg = xfw->Alloc_msg( 2048 ); // nothing back, need a new message to send
+ msg = xfw->Alloc_msg( 2048 );
}
if( delay > 0 ) {