From 3a2533f97b9ebee99a718530d111278cedb613d1 Mon Sep 17 00:00:00 2001 From: "E. Scott Daniels" Date: Wed, 22 Apr 2020 12:40:27 -0400 Subject: [PATCH] Add user guide 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 Change-Id: Ib9d36e2f84bfb29f33cdb877abd3927b205fa991 --- doc/README | 12 + doc/src/Makefile | 18 + doc/src/README | 17 + doc/src/lib/front_junk.im | 102 ++++++ doc/src/lib/generic_ps.im | 102 ++++++ doc/src/lib/license.im | 28 ++ doc/src/lib/markdown.im | 134 +++++++ doc/src/lib/raw_license.im | 19 + doc/src/lib/roff.im | 88 +++++ doc/src/lib/rst.im | 120 +++++++ doc/src/lib/setup.im | 65 ++++ doc/src/lib/txt.im | 108 ++++++ doc/src/user/.gitignore | 5 + doc/src/user/Makefile | 70 ++++ doc/src/user/cpp_frame.im | 348 +++++++++++++++++++ doc/src/user/example1.im | 57 +++ doc/src/user/example2.im | 45 +++ doc/src/user/example3.im | 35 ++ doc/src/user/user_guide.xfm | 60 ++++ docs/.gitignore | 2 + docs/_static/logo.png | Bin 0 -> 43935 bytes docs/conf.py | 6 + docs/conf.yaml | 3 + docs/favicon.ico | Bin 0 -> 15086 bytes docs/index.rst | 26 ++ docs/rel-notes.rst | 14 + docs/requirements-docs.txt | 5 + docs/user_guide.rst | 831 ++++++++++++++++++++++++++++++++++++++++++++ examples/rmr_dump.cpp | 45 ++- examples/xapp_t1.cpp | 60 ++-- examples/xapp_t2.cpp | 58 ++-- 31 files changed, 2409 insertions(+), 74 deletions(-) create mode 100644 doc/README create mode 100644 doc/src/Makefile create mode 100644 doc/src/README create mode 100644 doc/src/lib/front_junk.im create mode 100644 doc/src/lib/generic_ps.im create mode 100644 doc/src/lib/license.im create mode 100644 doc/src/lib/markdown.im create mode 100644 doc/src/lib/raw_license.im create mode 100644 doc/src/lib/roff.im create mode 100644 doc/src/lib/rst.im create mode 100644 doc/src/lib/setup.im create mode 100644 doc/src/lib/txt.im create mode 100644 doc/src/user/.gitignore create mode 100644 doc/src/user/Makefile create mode 100644 doc/src/user/cpp_frame.im create mode 100644 doc/src/user/example1.im create mode 100644 doc/src/user/example2.im create mode 100644 doc/src/user/example3.im create mode 100644 doc/src/user/user_guide.xfm create mode 100644 docs/.gitignore create mode 100644 docs/_static/logo.png create mode 100644 docs/conf.py create mode 100644 docs/conf.yaml create mode 100644 docs/favicon.ico create mode 100644 docs/index.rst create mode 100644 docs/rel-notes.rst create mode 100644 docs/requirements-docs.txt create mode 100644 docs/user_guide.rst diff --git a/doc/README b/doc/README new file mode 100644 index 0000000..4edb62e --- /dev/null +++ b/doc/README @@ -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 (/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 index 0000000..f36ffd1 --- /dev/null +++ b/doc/src/Makefile @@ -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 index 0000000..4305866 --- /dev/null +++ b/doc/src/README @@ -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 index 0000000..718cc6a --- /dev/null +++ b/doc/src/lib/front_junk.im @@ -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 index 0000000..bea4598 --- /dev/null +++ b/doc/src/lib/generic_ps.im @@ -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 index 0000000..3a9af17 --- /dev/null +++ b/doc/src/lib/license.im @@ -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 index 0000000..c8f996c --- /dev/null +++ b/doc/src/lib/markdown.im @@ -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   +.** ----------------------------------------------- + +.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 index 0000000..2334f0a --- /dev/null +++ b/doc/src/lib/raw_license.im @@ -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 index 0000000..2c579aa --- /dev/null +++ b/doc/src/lib/roff.im @@ -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 index 0000000..f20165e --- /dev/null +++ b/doc/src/lib/rst.im @@ -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 tags in the resulting HTML, but
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 index 0000000..a99d6d7 --- /dev/null +++ b/doc/src/lib/setup.im @@ -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 index 0000000..684a60b --- /dev/null +++ b/doc/src/lib/txt.im @@ -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 index 0000000..aa1acd1 --- /dev/null +++ b/doc/src/user/.gitignore @@ -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 index 0000000..729b7fb --- /dev/null +++ b/doc/src/user/Makefile @@ -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 index 0000000..aa4b166 --- /dev/null +++ b/doc/src/user/cpp_frame.im @@ -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 + #include + int main( ) { + std::unique_ptr xapp; + char* listen_port = (char *) "4560"; //RMR listen port + bool wait4table = true; // wait for a route table + + xapp = std::unique_ptr( + new Xapp( listen_port, wait4table ) ); + } +&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 + #include + long m1000_count = 0; // message counters, one for each type + long m1001_count = 0; + + // callback function that will increase the appropriate counter + void cbf( Message& mbuf, int mtype, int subid, int len, + Msg_component payload, void* data ) { + long* counter; + + if( (counter = (long *) data) != NULL ) { + (*counter)++; + } + } + + int main( ) { + std::unique_ptr xapp; + char* listen_port = (char *) "4560"; + bool wait4table = false; + + xapp = std::unique_ptr( + new Xapp( listen_port, wait4table ) ); + + // register the same callback function for both msg types + xapp->Add_msg_cb( 1000, cbf, (void *) &m1000_count ); + xapp->Add_msg_cb( 1001, cbf, (void *) &m1001_count ); + + 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 response ); + + bool Send_response( int response_len, std::shared_ptr 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 payload ); + + bool Send_msg( int mtype, int subid, int payload_len, + unsigned char* payload ); + + bool Send_msg( int payload_len, + std::shared_ptr payload ); + + bool Send_msg( int payload_len, 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 index 0000000..f54af6d --- /dev/null +++ b/doc/src/user/example1.im @@ -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 index 0000000..90a7f51 --- /dev/null +++ b/doc/src/user/example2.im @@ -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 index 0000000..e25f03e --- /dev/null +++ b/doc/src/user/example3.im @@ -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 index 0000000..c34538f --- /dev/null +++ b/doc/src/user/user_guide.xfm @@ -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 index 0000000..89d0765 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +/_build +/out diff --git a/docs/_static/logo.png b/docs/_static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c3b6ce56468d87a3d9463ee75297b3895fc9a414 GIT binary patch literal 43935 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

r@f1gRWk|A*%4`3la7*fhEBO++ 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 literal 0 HcmV?d00001 diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..922e22f --- /dev/null +++ b/docs/conf.py @@ -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 index 0000000..85e0266 --- /dev/null +++ b/docs/conf.yaml @@ -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 index 0000000000000000000000000000000000000000..00b0fd0ef0b4e78fbb8cdb413ce84561dfeb404f GIT binary patch literal 15086 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

4K$ruCE1 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 XY+}&}KC=mD;X?Iq*l*cA?B4$ePY*3q literal 0 HcmV?d00001 diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..37eb834 --- /dev/null +++ b/docs/index.rst @@ -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 index 0000000..2bb881c --- /dev/null +++ b/docs/rel-notes.rst @@ -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 index 0000000..09a0c1c --- /dev/null +++ b/docs/requirements-docs.txt @@ -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 index 0000000..4c5836a --- /dev/null +++ b/docs/user_guide.rst @@ -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 + #include + int main( ) { + std::unique_ptr xapp; + char* listen_port = (char *) "4560"; //RMR listen port + bool wait4table = true; // wait for a route table + xapp = std::unique_ptr( + new Xapp( listen_port, wait4table ) ); + } + + + Figure 1: Creating an xAPP instance. + + 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 + #include + long m1000_count = 0; // message counters, one for each type + long m1001_count = 0; + // callback function that will increase the appropriate counter + void cbf( Message& mbuf, int mtype, int subid, int len, + Msg_component payload, void* data ) { + long* counter; + if( (counter = (long *) data) != NULL ) { + (*counter)++; + } + } + int main( ) { + std::unique_ptr xapp; + char* listen_port = (char *) "4560"; + bool wait4table = false; + xapp = std::unique_ptr( + new Xapp( listen_port, wait4table ) ); + // register the same callback function for both msg types + xapp->Add_msg_cb( 1000, cbf, (void *) &m1000_count ); + xapp->Add_msg_cb( 1001, cbf, (void *) &m1001_count ); + 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 response ); + bool Send_response( int response_len, std::shared_ptr 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 payload ); + bool Send_msg( int mtype, int subid, int payload_len, + unsigned char* payload ); + bool Send_msg( int payload_len, + std::shared_ptr payload ); + bool Send_msg( int payload_len, 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 + #include + #include + #include "ricxfcpp/xapp.hpp" + /* + Information that the callback needs outside + of what is given to it via parms on a call + by the framework. + */ + typedef struct { + int vlevel; // verbosity level + bool forward; // if true, message is forwarded + int stats_freq; // header/stats after n messages + std::atomic pcount; // messages processed + std::atomic icount; // messages ignored + std::atomic hdr; // number of messages before next header + } cb_info_t; + // ---------------------------------------------------------------------- + /* + Dump bytes to tty. + */ + void dump( unsigned const char* buf, int len ) { + int i; + int j; + char cheater[17]; + fprintf( stdout, " 0000 | " ); + j = 0; + for( i = 0; i < len; i++ ) { + cheater[j++] = isprint( buf[i] ) ? buf[i] : '.'; + fprintf( stdout, "%02x ", buf[i] ); + if( j == 16 ) { + cheater[j] = 0; + fprintf( stdout, " | %s\\n %04x | ", cheater, i+1 ); + j = 0; + } + } + if( j ) { + i = 16 - (i % 16); + for( ; i > 0; i-- ) { + fprintf( stdout, " " ); + } + cheater[j] = 0; + fprintf( stdout, " | %s\\n", cheater ); + } + } + /* + generate stats when the hdr count reaches 0. Only one active + thread will ever see it be exactly 0, so this is thread safe. + */ + void stats( cb_info_t& cbi ) { + int curv; // current stat trigger value + curv = cbi.hdr--; + if( curv == 0 ) { // stats when we reach 0 + fprintf( stdout, "ignored: %ld processed: %ld\\n", + cbi.icount.load(), cbi.pcount.load() ); + if( cbi.vlevel > 0 ) { + fprintf( stdout, "\\n %5s %5s %2s %5s\\n", + "MTYPE", "SUBID", "ST", "PLLEN" ); + } + 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, " %-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 x; + char* port = (char *) "4560"; + int ai = 1; // arg processing index + cb_info_t* cbi; + int ncb = 0; // number of callbacks registered + int mtype; + int nthreads = 1; + cbi = (cb_info_t *) malloc( sizeof( *cbi ) ); + cbi->pcount = 0; + cbi->icount = 0; + cbi->stats_freq = 10; + ai = 1; + // very simple flag parsing (no error/bounds checking) + while( ai < argc ) { + if( argv[ai][0] != '-' ) { // break on first non-flag + break; + } + // very simple arg parsing; each must be separate -x -y not -xy. + switch( argv[ai][1] ) { + case 'f': // enable packet forwarding + cbi->forward = true; + break; + case 'p': // define port + port = argv[ai+1]; + ai++; + break; + case 's': // stats frequency + cbi->stats_freq = atoi( argv[ai+1] ); + if( cbi->stats_freq < 5 ) { // enforce sanity + cbi->stats_freq = 5; + } + ai++; + break; + case 't': // thread count + nthreads = atoi( argv[ai+1] ); + if( nthreads < 1 ) { + nthreads = 1; + } + ai++; + break; + case 'v': // simple verbose bump + cbi->vlevel++; + break; + case 'V': // explicit verbose level + cbi->vlevel = atoi( argv[ai+1] ); + ai++; + break; + default: + fprintf( stderr, "unrecognised option: %s\\n", argv[ai] ); + fprintf( stderr, "usage: %s [-f] [-p port] " + "[-s stats-freq] [-t thread-count] " + "[-v | -V n] msg-type1 ... msg-typen\\n", + argv[0] ); + fprintf( stderr, "\\tstats frequency is based on # of messages received\\n" ); + fprintf( stderr, "\\tverbose levels (-V) 0 counts only, " + "1 message info 2 payload dump\\n" ); + exit( 1 ); + } + ai++; + } + cbi->hdr = cbi->stats_freq; + fprintf( stderr, " listening on port: %s\\n", port ); + // create xapp, wait for route table if forwarding + x = std::unique_ptr( new Xapp( port, cbi->forward ) ); + // register callback for each type on the command line + while( ai < argc ) { + mtype = atoi( argv[ai] ); + ai++; + fprintf( stderr, " capturing messages for type %d\\n", mtype ); + x->Add_msg_cb( mtype, cb1, cbi ); + ncb++; + } + if( ncb < 1 ) { + fprintf( stderr, " no message types specified on the command line\\n" ); + exit( 1 ); + } + x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi ); // register default cb + fprintf( stderr, " starting driver\\n" ); + x->Run( nthreads ); + // return from run() is not expected, but some compilers might + // compilain if there isn't a return value here. + return 0; + } + + + 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 + #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, " listening on port: %s\\n", port ); + fprintf( stderr, " starting %d threads\\n", nthreads ); + x = new Xapp( port, true ); + x->Add_msg_cb( 1, cb1, NULL ); // register callbacks + x->Add_msg_cb( 2, cb2, NULL ); + x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL ); + x->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 + #include + #include + #include + #include + #include "ricxfcpp/xapp.hpp" + extern int main( int argc, char** argv ) { + std::unique_ptr xfw; + std::unique_ptr msg; + Msg_component payload; // special type of unique pointer to the payload + 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, " response timeout set to: %d\\n", response_to ); + fprintf( stderr, " listening on port: %s\\n", port ); + // get an instance and wait for a route table to be loaded + xfw = std::unique_ptr( new Xapp( port, true ) ); + msg = xfw->Alloc_msg( 2048 ); + for( i = 0; i < 100; i++ ) { + mtype++; + if( mtype > 10 ) { + mtype = 0; + } + // we'll reuse a received message; get max size + sz = msg->Get_available_size(); + // direct access to payload; add something silly + payload = msg->Get_payload(); + len = snprintf( (char *) payload.get(), sz, "This is message %d\\n", i ); + // payload updated in place, prevent copy by passing nil + if ( ! msg->Send_msg( mtype, Message::NO_SUBID, len, NULL )) { + fprintf( stderr, " send failed: %d\\n", i ); + } + // receive anything that might come back + msg = xfw->Receive( response_to ); + if( msg != NULL ) { + rmtype = msg->Get_mtype(); + payload = msg->Get_payload(); + fprintf( stderr, "got: mtype=%d payload=(%s)\\n", + rmtype, (char *) payload.get() ); + } else { + msg = xfw->Alloc_msg( 2048 ); + } + if( delay > 0 ) { + usleep( delay ); + } + } + } + + + Figure 10: Simple looping sender application. + diff --git a/examples/rmr_dump.cpp b/examples/rmr_dump.cpp index 5cced21..52fe23d 100644 --- a/examples/rmr_dump.cpp +++ b/examples/rmr_dump.cpp @@ -27,9 +27,9 @@ on by the application. The verbosity level may be used to increase the amount of detail given for the tracked messages. - This writes to the TTY which is slow, so do not expect it to be able + This writes to the TTY which is slow, so do not expect it to be able to process and report on a high rate of messages. Also, forwarded - messages will reach the intended target, however if the target + messages will reach the intended target, however if the target attempts to send a response the response will come back to THIS application, and not the message origination; this cannot be a bit of middleware in it's current form. @@ -37,7 +37,12 @@ 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 #include #include @@ -50,15 +55,15 @@ by the framework. */ typedef struct { - int vlevel; // verbosity level - bool forward; // if true, message is forwarded - int stats_freq; // header/stats after n messages - std::atomic pcount; // messages processed - std::atomic icount; // messages ignored - std::atomic hdr; // number of messages before next header + int vlevel; // verbosity level + bool forward; // if true, message is forwarded + int stats_freq; // header/stats after n messages + std::atomic pcount; // messages processed + std::atomic icount; // messages ignored + std::atomic hdr; // number of messages before next header } cb_info_t; -// ---------------------------------------------------------- +// ---------------------------------------------------------------------- /* Dump bytes to tty. @@ -73,10 +78,10 @@ void dump( unsigned const char* buf, int len ) { for( i = 0; i < len; i++ ) { cheater[j++] = isprint( buf[i] ) ? buf[i] : '.'; fprintf( stdout, "%02x ", buf[i] ); - + if( j == 16 ) { cheater[j] = 0; - fprintf( stdout, " | %s\n %04x | ", cheater, i+1 ); + fprintf( stdout, " | %s\n %04x | ", cheater, i+1 ); j = 0; } } @@ -87,7 +92,7 @@ void dump( unsigned const char* buf, int len ) { fprintf( stdout, " " ); } cheater[j] = 0; - fprintf( stdout, " | %s\n", cheater ); + fprintf( stdout, " | %s\n", cheater ); } } @@ -101,10 +106,10 @@ void stats( cb_info_t& cbi ) { curv = cbi.hdr--; if( curv == 0 ) { // stats when we reach 0 - fprintf( stdout, "ignored: %ld processed: %ld\n", + fprintf( stdout, "ignored: %ld processed: %ld\n", cbi.icount.load(), cbi.pcount.load() ); if( cbi.vlevel > 0 ) { - fprintf( stdout, "\n %5s %5s %2s %5s\n", + fprintf( stdout, "\n %5s %5s %2s %5s\n", "MTYPE", "SUBID", "ST", "PLLEN" ); } @@ -219,9 +224,13 @@ int main( int argc, char** argv ) { default: fprintf( stderr, "unrecognised option: %s\n", argv[ai] ); - fprintf( stderr, "usage: %s [-f] [-p port] [-s stats-freq] [-t thread-count] [-v | -V n] msg-type1 ... msg-typen\n", argv[0] ); - fprintf( stderr, "\tstats frequency is in number of messages received\n" ); - fprintf( stderr, "\tverbose levels (-V) 0 counts only, 1 message info 2 payload dump\n" ); + fprintf( stderr, "usage: %s [-f] [-p port] " + "[-s stats-freq] [-t thread-count] " + "[-v | -V n] msg-type1 ... msg-typen\n", + argv[0] ); + fprintf( stderr, "\tstats frequency is based on # of messages received\n" ); + fprintf( stderr, "\tverbose levels (-V) 0 counts only, " + "1 message info 2 payload dump\n" ); exit( 1 ); } @@ -235,7 +244,7 @@ int main( int argc, char** argv ) { x = std::unique_ptr( 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, " capturing messages for type %d\n", mtype ); diff --git a/examples/xapp_t1.cpp b/examples/xapp_t1.cpp index e525891..51b3ba6 100644 --- a/examples/xapp_t1.cpp +++ b/examples/xapp_t1.cpp @@ -23,8 +23,8 @@ Abstract: This is a simple demo xapp which registers 3 callback functions (2 for specific message types, and one default callback). It defaults to starting 1 thread, but can cause the xapp environment - to start n listeners. When running 1 thread it should emit - message countes ever few seconds with a crudly computed msg/sec + to start n listeners. When running 1 thread it should emit + message countes ever few seconds with a crudly computed msg/sec receive rate value. In addition, the callback for message type 1 will send two response @@ -34,17 +34,20 @@ 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 #include "ricxfcpp/message.hpp" #include "ricxfcpp/msg_component.hpp" #include "ricxfcpp/xapp.hpp" -// ---------------------------------------------------------- - -// counts/time values for crude rate estimation; only valid when threads == 1 +// counts; not thread safe long cb1_count = 0; long cb2_count = 0; long cbd_count = 0; @@ -52,34 +55,28 @@ long cbd_count = 0; long cb1_lastts = 0; long cb1_lastc = 0; -void cb1( Message& mbuf, int mtype, int subid, int len, Msg_component payload, void* data ) { +// respond with 2 messages for each type 1 received +void cb1( Message& mbuf, int mtype, int subid, int len, + Msg_component payload, void* data ) { long now; long total_count; - //fprintf( stderr, "callback 1 got a message type = %d len = %d\n", mtype, len ); - mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK1\n" ); // validate that we can use the same buffer for 2 rts calls + // illustrate that we can use the same buffer for 2 rts calls + mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK1\n" ); mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\n" ); + cb1_count++; - - now = time( NULL ); - if( now - cb1_lastts > 5 ) { // crude rate estimation starting with second timer pop - if( cb1_lastts ) { - total_count = cb1_count + cb2_count; - fprintf( stderr, "cb1: %ld diff=%ld ~rate=%ld\n", total_count, now - cb1_lastts, (total_count-cb1_lastc)/(now - cb1_lastts) ); - cb1_lastc = total_count; - } - cb1_lastts = now; - } } -void cb2( Message& mbuf, int mtype, int subid, int len, Msg_component payload, void* data ) { - //fprintf( stderr, "callback 2 got a message type = %d len = %d\n", mtype, len ); - //mbuf.Send_msg( 101, -1, 4, (unsigned char *) "HI\n" ); // send, including the trailing 0 +// just count messages +void cb2( Message& mbuf, int mtype, int subid, int len, + Msg_component payload, void* data ) { cb2_count++; } -void cbd( Message& mbuf, int mtype, int subid, int len, Msg_component payload, void* data ) { - //fprintf( stderr, "default callback got a message type = %d len = %d\n", mtype, len ); +// default to count all unrecognised messages +void cbd( Message& mbuf, int mtype, int subid, int len, + Msg_component payload, void* data ) { cbd_count++; } @@ -89,15 +86,15 @@ int main( int argc, char** argv ) { int ai = 1; // arg processing index int nthreads = 1; - ai = 1; - while( ai < argc ) { // very simple flag processing (no bounds/error checking) + // very simple flag processing (no bounds/error checking) + while( ai < argc ) { if( argv[ai][0] != '-' ) { break; } switch( argv[ai][1] ) { // we only support -x so -xy must be -x -y - case 'p': - port = argv[ai+1]; + case 'p': + port = argv[ai+1]; ai++; break; @@ -109,14 +106,15 @@ int main( int argc, char** argv ) { ai++; } - + fprintf( stderr, " listening on port: %s\n", port ); fprintf( stderr, " starting %d threads\n", nthreads ); x = new Xapp( port, true ); - x->Add_msg_cb( 1, cb1, NULL ); + x->Add_msg_cb( 1, cb1, NULL ); // register callbacks x->Add_msg_cb( 2, cb2, NULL ); x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL ); - x->Run( nthreads ); + x->Run( nthreads ); // let framework drive + // control should not return } diff --git a/examples/xapp_t2.cpp b/examples/xapp_t2.cpp index dd24d2b..67aa72f 100644 --- a/examples/xapp_t2.cpp +++ b/examples/xapp_t2.cpp @@ -26,8 +26,13 @@ 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 #include @@ -38,14 +43,13 @@ #include "ricxfcpp/xapp.hpp" -// ---------------------------------------------------------- - extern int main( int argc, char** argv ) { std::unique_ptr xfw; std::unique_ptr msg; - Msg_component payload; // special type of unique pointer to the payload + Msg_component payload; // special type of unique pointer to the payload int sz; + int len; int i; int ai; int response_to = 0; // max timeout wating for a response @@ -53,26 +57,29 @@ extern int main( int argc, char** argv ) { int mtype = 0; int rmtype; // received message type int delay = 1000000; // mu-sec delay; default 1s - - ai = 1; - while( ai < argc ) { // very simple flag processing (no bounds/error checking) + + // very simple flag processing (no bounds/error checking) + while( ai < argc ) { if( argv[ai][0] != '-' ) { break; } - switch( argv[ai][1] ) { // we only support -x so -xy must be -x -y - case 'd': // delay between messages (mu-sec) + // we only support -x so -xy must be -x -y + switch( argv[ai][1] ) { + // delay between messages (mu-sec) + case 'd': delay = atoi( argv[ai+1] ); ai++; break; - - case 'p': - port = argv[ai+1]; + + case 'p': + port = argv[ai+1]; ai++; break; - case 't': // timeout in seconds; we need to convert to ms for rmr calls + // timeout in seconds; we need to convert to ms for rmr calls + case 't': response_to = atoi( argv[ai+1] ) * 1000; ai++; break; @@ -83,7 +90,8 @@ extern int main( int argc, char** argv ) { fprintf( stderr, " response timeout set to: %d\n", response_to ); fprintf( stderr, " listening on port: %s\n", port ); - xfw = std::unique_ptr( 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( new Xapp( port, true ) ); msg = xfw->Alloc_msg( 2048 ); for( i = 0; i < 100; i++ ) { @@ -92,27 +100,27 @@ extern int main( int argc, char** argv ) { mtype = 0; } - sz = msg->Get_available_size(); // we'll reuse a message if we received one back; ensure it's big enough - if( sz < 2048 ) { - fprintf( stderr, " 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, " send failed: %d\n", i ); } + // receive anything that might come back msg = xfw->Receive( response_to ); if( msg != NULL ) { rmtype = msg->Get_mtype(); payload = msg->Get_payload(); - fprintf( stderr, "got: mtype=%d payload=(%s)\n", rmtype, (char *) payload.get() ); + fprintf( stderr, "got: mtype=%d payload=(%s)\n", + rmtype, (char *) payload.get() ); } else { - msg = xfw->Alloc_msg( 2048 ); // nothing back, need a new message to send + msg = xfw->Alloc_msg( 2048 ); } if( delay > 0 ) { -- 2.16.6