Add user guide 97/3397/4
authorE. Scott Daniels <daniels@research.att.com>
Wed, 22 Apr 2020 16:40:27 +0000 (12:40 -0400)
committerE. Scott Daniels <daniels@research.att.com>
Wed, 22 Apr 2020 19:33:20 +0000 (15:33 -0400)
This change adds the document source for the user guide as well
as the mechanics needed to allow for scraping the repo for RTD
documents.

Issue-ID: RIC-148

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

31 files changed:
doc/README [new file with mode: 0644]
doc/src/Makefile [new file with mode: 0644]
doc/src/README [new file with mode: 0644]
doc/src/lib/front_junk.im [new file with mode: 0644]
doc/src/lib/generic_ps.im [new file with mode: 0644]
doc/src/lib/license.im [new file with mode: 0644]
doc/src/lib/markdown.im [new file with mode: 0644]
doc/src/lib/raw_license.im [new file with mode: 0644]
doc/src/lib/roff.im [new file with mode: 0644]
doc/src/lib/rst.im [new file with mode: 0644]
doc/src/lib/setup.im [new file with mode: 0644]
doc/src/lib/txt.im [new file with mode: 0644]
doc/src/user/.gitignore [new file with mode: 0644]
doc/src/user/Makefile [new file with mode: 0644]
doc/src/user/cpp_frame.im [new file with mode: 0644]
doc/src/user/example1.im [new file with mode: 0644]
doc/src/user/example2.im [new file with mode: 0644]
doc/src/user/example3.im [new file with mode: 0644]
doc/src/user/user_guide.xfm [new file with mode: 0644]
docs/.gitignore [new file with mode: 0644]
docs/_static/logo.png [new file with mode: 0644]
docs/conf.py [new file with mode: 0644]
docs/conf.yaml [new file with mode: 0644]
docs/favicon.ico [new file with mode: 0644]
docs/index.rst [new file with mode: 0644]
docs/rel-notes.rst [new file with mode: 0644]
docs/requirements-docs.txt [new file with mode: 0644]
docs/user_guide.rst [new file with mode: 0644]
examples/rmr_dump.cpp
examples/xapp_t1.cpp
examples/xapp_t2.cpp

diff --git a/doc/README b/doc/README
new file mode 100644 (file)
index 0000000..4edb62e
--- /dev/null
@@ -0,0 +1,12 @@
+
+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.
diff --git a/doc/src/Makefile b/doc/src/Makefile
new file mode 100644 (file)
index 0000000..f36ffd1
--- /dev/null
@@ -0,0 +1,18 @@
+#   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)
diff --git a/doc/src/README b/doc/src/README
new file mode 100644 (file)
index 0000000..4305866
--- /dev/null
@@ -0,0 +1,17 @@
+
+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.
+
+
diff --git a/doc/src/lib/front_junk.im b/doc/src/lib/front_junk.im
new file mode 100644 (file)
index 0000000..718cc6a
--- /dev/null
@@ -0,0 +1,102 @@
+.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
+
diff --git a/doc/src/lib/generic_ps.im b/doc/src/lib/generic_ps.im
new file mode 100644 (file)
index 0000000..bea4598
--- /dev/null
@@ -0,0 +1,102 @@
+.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
diff --git a/doc/src/lib/license.im b/doc/src/lib/license.im
new file mode 100644 (file)
index 0000000..3a9af17
--- /dev/null
@@ -0,0 +1,28 @@
+
+.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
diff --git a/doc/src/lib/markdown.im b/doc/src/lib/markdown.im
new file mode 100644 (file)
index 0000000..c8f996c
--- /dev/null
@@ -0,0 +1,134 @@
+.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 &nbsp;
+.** -----------------------------------------------
+
+.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
diff --git a/doc/src/lib/raw_license.im b/doc/src/lib/raw_license.im
new file mode 100644 (file)
index 0000000..2334f0a
--- /dev/null
@@ -0,0 +1,19 @@
+
+.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
+
diff --git a/doc/src/lib/roff.im b/doc/src/lib/roff.im
new file mode 100644 (file)
index 0000000..2c579aa
--- /dev/null
@@ -0,0 +1,88 @@
+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
+
diff --git a/doc/src/lib/rst.im b/doc/src/lib/rst.im
new file mode 100644 (file)
index 0000000..f20165e
--- /dev/null
@@ -0,0 +1,120 @@
+.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
diff --git a/doc/src/lib/setup.im b/doc/src/lib/setup.im
new file mode 100644 (file)
index 0000000..a99d6d7
--- /dev/null
@@ -0,0 +1,65 @@
+.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
diff --git a/doc/src/lib/txt.im b/doc/src/lib/txt.im
new file mode 100644 (file)
index 0000000..684a60b
--- /dev/null
@@ -0,0 +1,108 @@
+.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
+
diff --git a/doc/src/user/.gitignore b/doc/src/user/.gitignore
new file mode 100644 (file)
index 0000000..aa1acd1
--- /dev/null
@@ -0,0 +1,5 @@
+*.ps
+*.rst
+*.txt
+*.html
+*.sp
diff --git a/doc/src/user/Makefile b/doc/src/user/Makefile
new file mode 100644 (file)
index 0000000..729b7fb
--- /dev/null
@@ -0,0 +1,70 @@
+#==================================================================================
+#   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:
+
diff --git a/doc/src/user/cpp_frame.im b/doc/src/user/cpp_frame.im
new file mode 100644 (file)
index 0000000..aa4b166
--- /dev/null
@@ -0,0 +1,348 @@
+
+.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.
diff --git a/doc/src/user/example1.im b/doc/src/user/example1.im
new file mode 100644 (file)
index 0000000..f54af6d
--- /dev/null
@@ -0,0 +1,57 @@
+.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
+
diff --git a/doc/src/user/example2.im b/doc/src/user/example2.im
new file mode 100644 (file)
index 0000000..90a7f51
--- /dev/null
@@ -0,0 +1,45 @@
+.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
diff --git a/doc/src/user/example3.im b/doc/src/user/example3.im
new file mode 100644 (file)
index 0000000..e25f03e
--- /dev/null
@@ -0,0 +1,35 @@
+.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
+
diff --git a/doc/src/user/user_guide.xfm b/doc/src/user/user_guide.xfm
new file mode 100644 (file)
index 0000000..c34538f
--- /dev/null
@@ -0,0 +1,60 @@
+.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
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644 (file)
index 0000000..89d0765
--- /dev/null
@@ -0,0 +1,2 @@
+/_build
+/out
diff --git a/docs/_static/logo.png b/docs/_static/logo.png
new file mode 100644 (file)
index 0000000..c3b6ce5
Binary files /dev/null and b/docs/_static/logo.png differ
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644 (file)
index 0000000..922e22f
--- /dev/null
@@ -0,0 +1,6 @@
+from docs_conf.conf import *
+linkcheck_ignore = [
+    'http://localhost.*',
+    'http://127.0.0.1.*',
+    'https://gerrit.o-ran-sc.org.*'
+]
diff --git a/docs/conf.yaml b/docs/conf.yaml
new file mode 100644 (file)
index 0000000..85e0266
--- /dev/null
@@ -0,0 +1,3 @@
+---
+project_cfg: oran
+project: portal-ric-xappframe-cpp
diff --git a/docs/favicon.ico b/docs/favicon.ico
new file mode 100644 (file)
index 0000000..00b0fd0
Binary files /dev/null and b/docs/favicon.ico differ
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644 (file)
index 0000000..37eb834
--- /dev/null
@@ -0,0 +1,26 @@
+
+.. 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`
diff --git a/docs/rel-notes.rst b/docs/rel-notes.rst
new file mode 100644 (file)
index 0000000..2bb881c
--- /dev/null
@@ -0,0 +1,14 @@
+
+.. 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
+
diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt
new file mode 100644 (file)
index 0000000..09a0c1c
--- /dev/null
@@ -0,0 +1,5 @@
+sphinx
+sphinx-rtd-theme
+sphinxcontrib-httpdomain
+recommonmark
+lfdocs-conf
diff --git a/docs/user_guide.rst b/docs/user_guide.rst
new file mode 100644 (file)
index 0000000..4c5836a
--- /dev/null
@@ -0,0 +1,831 @@
+     
+.. 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. 
+       
index 5cced21..52fe23d 100644 (file)
@@ -27,9 +27,9 @@
                                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.
@@ -73,10 +78,10 @@ void dump( unsigned const char* buf, int len ) {
        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;
                }
        }
@@ -87,7 +92,7 @@ void dump( unsigned const char* buf, int len ) {
                        fprintf( stdout, "   " );
                }
                cheater[j] = 0;
-               fprintf( stdout, " | %s\n", cheater );  
+               fprintf( stdout, " | %s\n", cheater );
        }
 }
 
@@ -101,10 +106,10 @@ void stats( cb_info_t& cbi ) {
        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" );
                }
 
@@ -219,9 +224,13 @@ int main( int argc, char** argv ) {
 
                        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 );
                }
 
@@ -235,7 +244,7 @@ int main( int argc, char** argv ) {
        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 );
index e525891..51b3ba6 100644 (file)
@@ -23,8 +23,8 @@
        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;
@@ -52,34 +55,28 @@ 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++;
 }
 
@@ -89,15 +86,15 @@ int main( int argc, char** argv ) {
        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;
 
@@ -109,14 +106,15 @@ int main( int argc, char** argv ) {
 
                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
 }
index dd24d2b..67aa72f 100644 (file)
 
        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
@@ -53,26 +57,29 @@ extern int main( int argc, char** argv ) {
        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;
@@ -83,7 +90,8 @@ extern int main( int argc, char** argv ) {
        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++ ) {
@@ -92,27 +100,27 @@ extern int main( int argc, char** argv ) {
                        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 ) {