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.
 
                                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
                                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.
                                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
 
        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>
 #include <stdio.h>
 #include <unistd.h>
 #include <atomic>
        by the framework.
 */
 typedef struct {
        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;
 
 } cb_info_t;
 
-// ----------------------------------------------------------
+// ----------------------------------------------------------------------
 
 /*
        Dump bytes to tty.
 
 /*
        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] );
        for( i = 0; i < len; i++ ) {
                cheater[j++] =  isprint( buf[i] ) ? buf[i] : '.';
                fprintf( stdout, "%02x ", buf[i] );
-                       
+
                if( j == 16 ) {
                        cheater[j] = 0;
                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;
                }
        }
                        j = 0;
                }
        }
@@ -87,7 +92,7 @@ void dump( unsigned const char* buf, int len ) {
                        fprintf( stdout, "   " );
                }
                cheater[j] = 0;
                        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
        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 ) {
                        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" );
                }
 
                                "MTYPE", "SUBID", "ST", "PLLEN" );
                }
 
@@ -219,9 +224,13 @@ int main( int argc, char** argv ) {
 
                        default:
                                fprintf( stderr, "unrecognised option: %s\n", argv[ai] );
 
                        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 );
                }
 
                                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
        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 );
                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
        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
                                receive rate value.
 
                                In addition, the callback for message type 1 will send two response
 
        Date:           18 March 2020
        Author:         E. Scott Daniels
 
        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"
 
 #include <stdio.h>
 
 #include "ricxfcpp/message.hpp"
 #include "ricxfcpp/msg_component.hpp"
 #include "ricxfcpp/xapp.hpp"
 
-// ----------------------------------------------------------
-
-// counts/time values for crude rate estimation; only valid when threads == 1
+// counts; not thread safe
 long cb1_count = 0;
 long cb2_count = 0;
 long cbd_count = 0;
 long cb1_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;
 
 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;
 
        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" );
        mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\n" );
+
        cb1_count++;
        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++;
 }
 
        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++;
 }
 
        cbd_count++;
 }
 
@@ -89,15 +86,15 @@ int main( int argc, char** argv ) {
        int ai = 1;                                                     // arg processing index
        int nthreads = 1;
 
        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
                if( argv[ai][0] != '-' )  {
                        break;
                }
 
                switch( argv[ai][1] ) {                 // we only support -x so -xy must be -x -y
-                       case 'p': 
-                               port = argv[ai+1];      
+                       case 'p':
+                               port = argv[ai+1];
                                ai++;
                                break;
 
                                ai++;
                                break;
 
@@ -109,14 +106,15 @@ int main( int argc, char** argv ) {
 
                ai++;
        }
 
                ai++;
        }
-       
+
        fprintf( stderr, "<XAPP> listening on port: %s\n", port );
        fprintf( stderr, "<XAPP> starting %d threads\n", nthreads );
 
        x = new Xapp( port, true );
        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->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
 
        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 <stdio.h>
 #include <string.h>
 
 #include "ricxfcpp/xapp.hpp"
 
 
 #include "ricxfcpp/xapp.hpp"
 
-// ----------------------------------------------------------
-
 extern int main( int argc, char** argv ) {
        std::unique_ptr<Xapp> xfw;
        std::unique_ptr<Message> msg;
 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     sz;
+       int len;
        int i;
        int ai;
        int response_to = 0;                            // max timeout wating for a response
        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
        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;
                }
 
                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;
                                delay = atoi( argv[ai+1] );
                                ai++;
                                break;
-                               
-                       case 'p': 
-                               port = argv[ai+1];      
+
+                       case 'p':
+                               port = argv[ai+1];
                                ai++;
                                break;
 
                                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;
                                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 );
 
        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++ ) {
        msg = xfw->Alloc_msg( 2048 );
 
        for( i = 0; i < 100; i++ ) {
@@ -92,27 +100,27 @@ extern int main( int argc, char** argv ) {
                        mtype = 0;
                }
 
                        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 );
                }
 
                        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();
                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 {
                } else {
-                       msg = xfw->Alloc_msg( 2048 );                           // nothing back, need a new message to send
+                       msg = xfw->Alloc_msg( 2048 );
                }
 
                if( delay > 0 ) {
                }
 
                if( delay > 0 ) {